diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0fe2da5a9..e0f5e7356 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -786,6 +786,7 @@ 9A0326D2375075871D2AB537 /* ResolveVerifiedUserSendFailureScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 574CB70E82D7EAEA538E4135 /* ResolveVerifiedUserSendFailureScreenViewModel.swift */; }; 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; }; 9A4E3D5AA44B041DAC3A0D81 /* OIDCAuthenticationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */; }; + 9A8E6FCD86B89970EC72EFD8 /* BugReportServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */; }; 9AC5F8142413862A9E3A2D98 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; }; 9B03943616A1147539DF7F08 /* RoomChangePermissionsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D041A857614A9AE13C7795 /* RoomChangePermissionsScreenViewModelTests.swift */; }; 9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */; }; @@ -2029,6 +2030,7 @@ 8F062DD2CCD95DC33528A16F /* KnockRequestProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestProxy.swift; sourceTree = ""; }; 8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = ""; }; 8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = ""; }; + 8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceMock.swift; sourceTree = ""; }; 8F841F219ACDFC1D3F42FEFB /* RoomChangeRolesScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelTests.swift; sourceTree = ""; }; 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenViewModelTests.swift; sourceTree = ""; }; 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; @@ -3215,6 +3217,7 @@ 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */, 4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */, 9FD7E851E2BA8C5A8D284B2A /* BannedRoomProxyMock.swift */, + 8F7FC9580CABF797A2E6213A /* BugReportServiceMock.swift */, E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */, 4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */, E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, @@ -6947,6 +6950,7 @@ 8D71E5E53F372202379BECCE /* BugReportScreenViewModel.swift in Sources */, B4A0C69370E6008A971463E7 /* BugReportScreenViewModelProtocol.swift in Sources */, 3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */, + 9A8E6FCD86B89970EC72EFD8 /* BugReportServiceMock.swift in Sources */, 172E6E9A612ADCF10A62CF13 /* BugReportServiceProtocol.swift in Sources */, E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */, 6BAD956B909A6E29F6CC6E7C /* ButtonStyle.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 78eb6d9dd..a59c53706 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -355,8 +355,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private static func setupServiceLocator(appSettings: AppSettings, appHooks: AppHooks) { ServiceLocator.shared.register(userIndicatorController: UserIndicatorController()) ServiceLocator.shared.register(appSettings: appSettings) - ServiceLocator.shared.register(bugReportService: BugReportService(withBaseURL: appSettings.bugReportServiceBaseURL, - applicationId: appSettings.bugReportApplicationId, + ServiceLocator.shared.register(bugReportService: BugReportService(baseURL: appSettings.bugReportServiceBaseURL, + applicationID: appSettings.bugReportApplicationID, sdkGitSHA: sdkGitSha(), maxUploadSize: appSettings.bugReportMaxUploadSize, appHooks: appHooks)) @@ -782,6 +782,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg } private static func setupSentry(appSettings: AppSettings) { + guard let bugReportSentryURL = appSettings.bugReportSentryURL else { return } + let options: Options = .init() #if DEBUG @@ -790,7 +792,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg options.enabled = appSettings.analyticsConsentState == .optedIn #endif - options.dsn = appSettings.bugReportSentryURL.absoluteString + options.dsn = bugReportSentryURL.absoluteString if AppSettings.isDevelopmentBuild { options.environment = "development" diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 3ddb5ed2e..778e13319 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -223,23 +223,21 @@ final class AppSettings { // MARK: - Bug report - let bugReportServiceBaseURL: URL! = URL(string: Secrets.rageshakeServerURL) - let bugReportSentryURL: URL! = URL(string: Secrets.sentryDSN) - // Use the name allocated by the bug report server - let bugReportApplicationId = "element-x-ios" + let bugReportServiceBaseURL: URL? = URL(string: Secrets.rageshakeServerURL)! // swiftlint:disable:this force_unwrapping + let bugReportSentryURL: URL? = URL(string: Secrets.sentryDSN)! // swiftlint:disable:this force_unwrapping + /// The name allocated by the bug report server + let bugReportApplicationID = "element-x-ios" /// The maximum size of the upload request. Default value is just below CloudFlare's max request size. let bugReportMaxUploadSize = 50 * 1024 * 1024 // MARK: - Analytics - /// The configuration to use for analytics. Set `isEnabled` to false to disable analytics. - /// - /// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork you will - /// need to regenerate the Secrets file with your PostHog server and API key before enabling. - let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.main.bundleIdentifier.starts(with: "io.element."), - host: Secrets.postHogHost, - apiKey: Secrets.postHogAPIKey, - termsURL: "https://element.io/cookie-policy") + /// The configuration to use for analytics. Set to `nil` to disable analytics. + let analyticsConfiguration: AnalyticsConfiguration? = AnalyticsConfiguration(host: Secrets.postHogHost, apiKey: Secrets.postHogAPIKey) + /// The URL to open with more information about analytics terms. When this is `nil` the "Learn more" link will be hidden. + private(set) var analyticsTermsURL: URL? = "https://element.io/cookie-policy" + /// Whether or not there the app is able ask for user consent to enable analytics or sentry reporting. + var canPromptForAnalytics: Bool { analyticsConfiguration != nil || bugReportSentryURL != nil } /// Whether the user has opted in to send analytics. @UserPreference(key: UserDefaultsKeys.analyticsConsentState, defaultValue: AnalyticsConsentState.unknown, storageType: .userDefaults(store)) diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index 7443c2769..e54333b1a 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -68,7 +68,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { // MARK: - Private private func showStartScreen() { - let parameters = AuthenticationStartScreenParameters(webRegistrationEnabled: appSettings.webRegistrationEnabled) + let parameters = AuthenticationStartScreenParameters(webRegistrationEnabled: appSettings.webRegistrationEnabled, + isBugReportServiceEnabled: bugReportService.isEnabled) let coordinator = AuthenticationStartScreenCoordinator(parameters: parameters) coordinator.actions diff --git a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift index 36a57c4e9..60a5d7ed1 100644 --- a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift @@ -379,7 +379,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { } private func presentAnalyticsPromptScreen() { - let coordinator = AnalyticsPromptScreenCoordinator(analytics: analyticsService, termsURL: appSettings.analyticsConfiguration.termsURL) + let coordinator = AnalyticsPromptScreenCoordinator(analytics: analyticsService, termsURL: appSettings.analyticsTermsURL) coordinator.actions .sink { [weak self] action in diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index add35a62f..16ec6d016 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -86,7 +86,8 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { navigationStackCoordinator = NavigationStackCoordinator() let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: parameters.userSession, - appSettings: parameters.appSettings)) + appSettings: parameters.appSettings, + isBugReportServiceEnabled: parameters.bugReportService.isEnabled)) settingsScreenCoordinator.actions .sink { [weak self] action in diff --git a/ElementX/Sources/Mocks/BugReportServiceMock.swift b/ElementX/Sources/Mocks/BugReportServiceMock.swift new file mode 100644 index 000000000..39f446caf --- /dev/null +++ b/ElementX/Sources/Mocks/BugReportServiceMock.swift @@ -0,0 +1,20 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Foundation + +extension BugReportServiceMock { + struct Configuration { + var isEnabled = true + } + + convenience init(_ configuration: Configuration) { + self.init() + + isEnabled = configuration.isEnabled + } +} diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 960ab5a15..957e22a28 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2130,6 +2130,11 @@ class BannedRoomProxyMock: BannedRoomProxyProtocol, @unchecked Sendable { } } class BugReportServiceMock: BugReportServiceProtocol, @unchecked Sendable { + var isEnabled: Bool { + get { return underlyingIsEnabled } + set(value) { underlyingIsEnabled = value } + } + var underlyingIsEnabled: Bool! var crashedLastRun: Bool { get { return underlyingCrashedLastRun } set(value) { underlyingCrashedLastRun = value } diff --git a/ElementX/Sources/Other/MapLibre/MapTilerConfiguration.swift b/ElementX/Sources/Other/MapLibre/MapTilerConfiguration.swift index a2b73c82f..a149a060e 100644 --- a/ElementX/Sources/Other/MapLibre/MapTilerConfiguration.swift +++ b/ElementX/Sources/Other/MapLibre/MapTilerConfiguration.swift @@ -13,17 +13,21 @@ import CoreLocation /// [FORKING.md](https://github.com/element-hq/element-x-ios/blob/develop/docs/FORKING.md#setup-the-location-sharing) struct MapTilerConfiguration { let baseURL: URL - let apiKey: String + /// The API key for fetching map tiles. When not set, location sharing will be disabled and + /// any received locations will be shown in the timeline with a generic blurred map image. + let apiKey: String? /// A MapLibre style ID for a light-mode map. let lightStyleID: String /// A MapLibre style ID for a dark-mode map. let darkStyleID: String + + var isEnabled: Bool { apiKey != nil } } extension MapTilerConfiguration: MapTilerURLBuilderProtocol { func dynamicMapURL(for style: MapTilerStyle) -> URL? { var url = makeNewURL(for: style) - url.appendPathComponent("style.json", conformingTo: .json) + url?.appendPathComponent("style.json", conformingTo: .json) return url } @@ -33,20 +37,22 @@ extension MapTilerConfiguration: MapTilerURLBuilderProtocol { size: CGSize, attribution: MapTilerAttributionPlacement) -> URL? { var url = makeNewURL(for: style) - url.appendPathComponent(String(format: "static/%f,%f,%f/%dx%d@2x.png", - coordinates.longitude, - coordinates.latitude, - zoomLevel, - Int(size.width), - Int(size.height)), - conformingTo: .png) - url.append(queryItems: [.init(name: "attribution", value: attribution.rawValue)]) + url?.appendPathComponent(String(format: "static/%f,%f,%f/%dx%d@2x.png", + coordinates.longitude, + coordinates.latitude, + zoomLevel, + Int(size.width), + Int(size.height)), + conformingTo: .png) + url?.append(queryItems: [.init(name: "attribution", value: attribution.rawValue)]) return url } // MARK: Private - private func makeNewURL(for style: MapTilerStyle) -> URL { + private func makeNewURL(for style: MapTilerStyle) -> URL? { + guard let apiKey else { return nil } + var url: URL = baseURL url.appendPathComponent(styleID(for: style), conformingTo: .item) url.append(queryItems: [URLQueryItem(name: "key", value: apiKey)]) diff --git a/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift b/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift index a332fdd84..d785c91de 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift @@ -55,7 +55,7 @@ private struct VisualListItemLabelStyle: LabelStyle { // MARK: - Previews struct VisualListItem_Previews: PreviewProvider, TestablePreview { - static let strings = AnalyticsPromptScreenStrings(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL) + static let strings = AnalyticsPromptScreenStrings(termsURL: ServiceLocator.shared.settings.analyticsTermsURL) @ViewBuilder static var testImage1: some View { diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift index 2cccb7ed8..5dbe1b76d 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift @@ -10,6 +10,7 @@ import SwiftUI struct AuthenticationStartScreenParameters { let webRegistrationEnabled: Bool + let isBugReportServiceEnabled: Bool } final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { @@ -22,7 +23,8 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { } init(parameters: AuthenticationStartScreenParameters) { - viewModel = AuthenticationStartScreenViewModel(webRegistrationEnabled: parameters.webRegistrationEnabled) + viewModel = AuthenticationStartScreenViewModel(webRegistrationEnabled: parameters.webRegistrationEnabled, + isBugReportServiceEnabled: parameters.isBugReportServiceEnabled) } // MARK: - Public diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift index d2e4e9153..1624c752a 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift @@ -26,6 +26,7 @@ enum AuthenticationStartScreenViewModelAction { struct AuthenticationStartScreenViewState: BindableState { let isWebRegistrationEnabled: Bool let isQRCodeLoginEnabled: Bool + let isBugReportServiceEnabled: Bool } enum AuthenticationStartScreenViewAction { diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift index 68654a11a..0fba37741 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift @@ -17,9 +17,10 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType actionsSubject.eraseToAnyPublisher() } - init(webRegistrationEnabled: Bool) { + init(webRegistrationEnabled: Bool, isBugReportServiceEnabled: Bool) { super.init(initialViewState: AuthenticationStartScreenViewState(isWebRegistrationEnabled: webRegistrationEnabled, - isQRCodeLoginEnabled: !ProcessInfo.processInfo.isiOSAppOnMac)) + isQRCodeLoginEnabled: !ProcessInfo.processInfo.isiOSAppOnMac, + isBugReportServiceEnabled: isBugReportServiceEnabled)) } override func process(viewAction: AuthenticationStartScreenViewAction) { diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift index b2dd29f93..110d8c22c 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift @@ -34,15 +34,17 @@ struct AuthenticationStartScreen: View { } .frame(maxHeight: .infinity) .safeAreaInset(edge: .bottom) { - Button { - context.send(viewAction: .reportProblem) - } label: { - Text(L10n.commonReportAProblem) - .font(.compound.bodySM) - .foregroundColor(.compound.textSecondary) - .padding(.bottom) + if context.viewState.isBugReportServiceEnabled { + Button { + context.send(viewAction: .reportProblem) + } label: { + Text(L10n.commonReportAProblem) + .font(.compound.bodySM) + .foregroundColor(.compound.textSecondary) + .padding(.bottom) + } + .frame(width: geometry.size.width) } - .frame(width: geometry.size.width) } } .navigationBarHidden(true) @@ -116,9 +118,18 @@ struct AuthenticationStartScreen: View { // MARK: - Previews struct AuthenticationStartScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = AuthenticationStartScreenViewModel(webRegistrationEnabled: true) + static let viewModel = makeViewModel() + static let bugReportDisabledViewModel = makeViewModel(isBugReportServiceEnabled: false) static var previews: some View { AuthenticationStartScreen(context: viewModel.context) + .previewDisplayName("Default") + AuthenticationStartScreen(context: bugReportDisabledViewModel.context) + .previewDisplayName("Bug report disabled") + } + + static func makeViewModel(isBugReportServiceEnabled: Bool = true) -> AuthenticationStartScreenViewModel { + AuthenticationStartScreenViewModel(webRegistrationEnabled: true, + isBugReportServiceEnabled: isBugReportServiceEnabled) } } diff --git a/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift b/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift index 7269c8b0d..11d7343b6 100644 --- a/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift +++ b/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift @@ -141,7 +141,7 @@ struct BugReportScreen_Previews: PreviewProvider, TestablePreview { static var previews: some View { NavigationStack { let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) - BugReportScreen(context: BugReportScreenViewModel(bugReportService: BugReportServiceMock(), + BugReportScreen(context: BugReportScreenViewModel(bugReportService: BugReportServiceMock(.init()), clientProxy: clientProxy, screenshot: nil, isModallyPresented: false).context) @@ -150,7 +150,7 @@ struct BugReportScreen_Previews: PreviewProvider, TestablePreview { NavigationStack { let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) - BugReportScreen(context: BugReportScreenViewModel(bugReportService: BugReportServiceMock(), + BugReportScreen(context: BugReportScreenViewModel(bugReportService: BugReportServiceMock(.init()), clientProxy: clientProxy, screenshot: Asset.Images.appLogo.image, isModallyPresented: false).context) diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index a64bd7298..5a15dab73 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -87,7 +87,9 @@ final class HomeScreenCoordinator: CoordinatorProtocol { func start() { #if !DEBUG - if bugReportService.crashedLastRun { + // Note: bugReportService.isEnabled doesn't determine if a user has opted in to Analytics/Sentry. + // Therefore we use lastCrashEventID as this will only be set if we have crash ID from Sentry. + if bugReportService.crashedLastRun, bugReportService.lastCrashEventID != nil { viewModel.presentCrashedLastRunAlert() } #endif diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenCoordinator.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenCoordinator.swift index f6fafb3f1..8861131cc 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenCoordinator.swift @@ -22,7 +22,7 @@ final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol { actionsSubject.eraseToAnyPublisher() } - init(analytics: AnalyticsService, termsURL: URL) { + init(analytics: AnalyticsService, termsURL: URL?) { self.analytics = analytics viewModel = AnalyticsPromptScreenViewModel(termsURL: termsURL) } diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenModels.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenModels.swift index 06e02e6a7..47c19894b 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenModels.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenModels.swift @@ -33,17 +33,21 @@ struct AnalyticsPromptScreenStrings { let point2 = L10n.screenAnalyticsPromptThirdPartySharing let point3 = L10n.screenAnalyticsPromptSettings - init(termsURL: URL) { + init(termsURL: URL?) { let content = AttributedString(L10n.screenAnalyticsPromptHelpUsImprove) - // Create the 'read terms' with a placeholder. - let linkPlaceholder = "{link}" - var readTerms = AttributedString(L10n.screenAnalyticsSettingsReadTerms(linkPlaceholder)) - var linkString = AttributedString(L10n.screenAnalyticsSettingsReadTermsContentLink) - linkString.link = termsURL - linkString.bold() - readTerms.replace(linkPlaceholder, with: linkString) - - optInContent = content + "\n\n" + readTerms + if let termsURL { + // Create the 'read terms' with a placeholder. + let linkPlaceholder = "{link}" + var readTerms = AttributedString(L10n.screenAnalyticsSettingsReadTerms(linkPlaceholder)) + var linkString = AttributedString(L10n.screenAnalyticsSettingsReadTermsContentLink) + linkString.link = termsURL + linkString.bold() + readTerms.replace(linkPlaceholder, with: linkString) + + optInContent = content + "\n\n" + readTerms + } else { + optInContent = content + } } } diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenViewModel.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenViewModel.swift index 7e25da238..30f3c7bb3 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenViewModel.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/AnalyticsPromptScreenViewModel.swift @@ -18,7 +18,7 @@ class AnalyticsPromptScreenViewModel: AnalyticsPromptScreenViewModelType, Analyt } /// Initialize a view model with the specified prompt type and app display name. - init(termsURL: URL) { + init(termsURL: URL?) { let promptStrings = AnalyticsPromptScreenStrings(termsURL: termsURL) super.init(initialViewState: AnalyticsPromptScreenViewState(strings: promptStrings)) } diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift index 3479e8f09..8d7a9f1e1 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift @@ -91,8 +91,17 @@ struct AnalyticsPromptScreen: View { // MARK: - Previews struct AnalyticsPromptScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = AnalyticsPromptScreenViewModel(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL) + static let viewModel = makeViewModel() + static let noTermsViewModel = makeViewModel(showTerms: false) + static var previews: some View { AnalyticsPromptScreen(context: viewModel.context) + .previewDisplayName("Default") + AnalyticsPromptScreen(context: noTermsViewModel.context) + .previewDisplayName("No terms") + } + + static func makeViewModel(showTerms: Bool = true) -> AnalyticsPromptScreenViewModel { + AnalyticsPromptScreenViewModel(termsURL: showTerms ? ServiceLocator.shared.settings.analyticsTermsURL : nil) } } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index f2442045d..6cc74a88e 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -74,6 +74,7 @@ struct ComposerToolbarViewState: BindableState { var audioRecorderState: AudioRecorderState var isRoomEncrypted: Bool + var isLocationSharingEnabled: Bool var bindings: ComposerToolbarViewStateBindings diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index baf6aee3f..99cbcd1c3 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -50,6 +50,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool completionSuggestionService: CompletionSuggestionServiceProtocol, mediaProvider: MediaProviderProtocol, mentionDisplayHelper: MentionDisplayHelper, + appSettings: AppSettings, analyticsService: AnalyticsService, composerDraftService: ComposerDraftServiceProtocol) { self.initialText = initialText @@ -65,6 +66,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool super.init(initialViewState: ComposerToolbarViewState(audioPlayerState: .init(id: .recorderPreview, title: L10n.commonVoiceMessage, duration: 0), audioRecorderState: .init(), isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted, + isLocationSharingEnabled: appSettings.mapTilerConfiguration.isEnabled, bindings: .init()), mediaProvider: mediaProvider) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index 32c98e334..24e981371 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -319,6 +319,7 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -369,6 +370,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.composerEmpty = focused @@ -387,6 +389,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.composerEmpty = focused @@ -405,6 +408,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.composerMode = .recordVoiceMessage(state: AudioRecorderState()) @@ -424,6 +428,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.composerMode = .previewVoiceMessage(state: AudioPlayerState(id: .recorderPreview, @@ -446,6 +451,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.composerMode = isLoading ? .reply(eventID: UUID().uuidString, @@ -470,6 +476,7 @@ extension ComposerToolbar { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) model.state.canSend = false diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift index c8a9a4664..351e144e5 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift @@ -47,12 +47,14 @@ struct RoomAttachmentPicker: View { } .accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPoll) - Button { - context.send(viewAction: .attach(.location)) - } label: { - Label(L10n.screenRoomAttachmentSourceLocation, icon: \.locationPin) + if context.viewState.isLocationSharingEnabled { + Button { + context.send(viewAction: .attach(.location)) + } label: { + Label(L10n.screenRoomAttachmentSourceLocation, icon: \.locationPin) + } + .accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerLocation) } - .accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerLocation) Button { context.send(viewAction: .attach(.file)) @@ -91,6 +93,7 @@ struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview { completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 344e72244..d14fbebd3 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -71,7 +71,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol { mediaProvider: parameters.mediaProvider, ongoingCallRoomIDPublisher: parameters.ongoingCallRoomIDPublisher, appMediator: parameters.appMediator, - appSettings: ServiceLocator.shared.settings, + appSettings: parameters.appSettings, analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController) @@ -99,6 +99,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol { completionSuggestionService: parameters.completionSuggestionService, mediaProvider: parameters.mediaProvider, mentionDisplayHelper: ComposerMentionDisplayHelper(timelineContext: timelineViewModel.context), + appSettings: parameters.appSettings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: parameters.composerDraftService) self.composerViewModel = composerViewModel diff --git a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenModels.swift b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenModels.swift index 1478336dc..bcf13ff69 100644 --- a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenModels.swift @@ -24,17 +24,21 @@ enum AnalyticsSettingsScreenViewAction { struct AnalyticsSettingsScreenStrings { let sectionFooter: AttributedString - init(termsURL: URL) { + init(termsURL: URL?) { let content = AttributedString(L10n.screenAnalyticsPromptHelpUsImprove) - // Create the 'read terms' with a placeholder. - let linkPlaceholder = "{link}" - var readTerms = AttributedString(L10n.screenAnalyticsSettingsReadTerms(linkPlaceholder)) - var linkString = AttributedString(L10n.screenAnalyticsSettingsReadTermsContentLink) - linkString.link = termsURL - linkString.bold() - readTerms.replace(linkPlaceholder, with: linkString) - - sectionFooter = content + "\n\n" + readTerms + if let termsURL { + // Create the 'read terms' with a placeholder. + let linkPlaceholder = "{link}" + var readTerms = AttributedString(L10n.screenAnalyticsSettingsReadTerms(linkPlaceholder)) + var linkString = AttributedString(L10n.screenAnalyticsSettingsReadTermsContentLink) + linkString.link = termsURL + linkString.bold() + readTerms.replace(linkPlaceholder, with: linkString) + + sectionFooter = content + "\n\n" + readTerms + } else { + sectionFooter = content + } } } diff --git a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenViewModel.swift index 90088f4b2..6dbcaab2a 100644 --- a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/AnalyticsSettingsScreenViewModel.swift @@ -16,7 +16,7 @@ class AnalyticsSettingsScreenViewModel: AnalyticsSettingsScreenViewModelType, An init(appSettings: AppSettings, analytics: AnalyticsService) { self.analytics = analytics - let strings = AnalyticsSettingsScreenStrings(termsURL: appSettings.analyticsConfiguration.termsURL) + let strings = AnalyticsSettingsScreenStrings(termsURL: appSettings.analyticsTermsURL) let bindings = AnalyticsSettingsScreenViewStateBindings(enableAnalytics: analytics.isEnabled) let state = AnalyticsSettingsScreenViewState(strings: strings, bindings: bindings) diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift index 718444626..ee3bf47da 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift @@ -11,6 +11,7 @@ import SwiftUI struct SettingsScreenCoordinatorParameters { let userSession: UserSessionProtocol let appSettings: AppSettings + let isBugReportServiceEnabled: Bool } enum SettingsScreenCoordinatorAction { @@ -43,7 +44,9 @@ final class SettingsScreenCoordinator: CoordinatorProtocol { // MARK: - Setup init(parameters: SettingsScreenCoordinatorParameters) { - viewModel = SettingsScreenViewModel(userSession: parameters.userSession) + viewModel = SettingsScreenViewModel(userSession: parameters.userSession, + appSettings: parameters.appSettings, + isBugReportServiceEnabled: parameters.isBugReportServiceEnabled) viewModel.actions .sink { [weak self] action in diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenModels.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenModels.swift index 78b3a1a8a..28a28350f 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenModels.swift @@ -44,6 +44,9 @@ struct SettingsScreenViewState: BindableState { var showSecuritySectionBadge = false var showBlockedUsers = false + let showAnalyticsSettings: Bool + + let isBugReportServiceEnabled: Bool var bindings = SettingsScreenViewStateBindings() } diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift index fb29e636f..b267a95d3 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift @@ -17,11 +17,13 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo actionsSubject.eraseToAnyPublisher() } - init(userSession: UserSessionProtocol) { + init(userSession: UserSessionProtocol, appSettings: AppSettings, isBugReportServiceEnabled: Bool) { super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID, userID: userSession.clientProxy.userID, showAccountDeactivation: userSession.clientProxy.canDeactivateAccount, - showDeveloperOptions: AppSettings.isDevelopmentBuild), + showDeveloperOptions: AppSettings.isDevelopmentBuild, + showAnalyticsSettings: appSettings.canPromptForAnalytics, + isBugReportServiceEnabled: isBugReportServiceEnabled), mediaProvider: userSession.mediaProvider) userSession.clientProxy.userAvatarURLPublisher diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift index 241e8bdb7..1a996e42d 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift @@ -140,19 +140,23 @@ struct SettingsScreen: View { }) .accessibilityIdentifier(A11yIdentifiers.settingsScreen.about) - ListRow(label: .default(title: L10n.commonReportAProblem, - icon: \.chatProblem), - kind: .navigationLink { - context.send(viewAction: .reportBug) - }) - .accessibilityIdentifier(A11yIdentifiers.settingsScreen.reportBug) + if context.viewState.isBugReportServiceEnabled { + ListRow(label: .default(title: L10n.commonReportAProblem, + icon: \.chatProblem), + kind: .navigationLink { + context.send(viewAction: .reportBug) + }) + .accessibilityIdentifier(A11yIdentifiers.settingsScreen.reportBug) + } - ListRow(label: .default(title: L10n.commonAnalytics, - icon: \.chart), - kind: .navigationLink { - context.send(viewAction: .analytics) - }) - .accessibilityIdentifier(A11yIdentifiers.settingsScreen.analytics) + if context.viewState.showAnalyticsSettings { + ListRow(label: .default(title: L10n.commonAnalytics, + icon: \.chart), + kind: .navigationLink { + context.send(viewAction: .analytics) + }) + .accessibilityIdentifier(A11yIdentifiers.settingsScreen.analytics) + } ListRow(label: .default(title: L10n.commonAdvancedSettings, icon: \.settings), @@ -230,18 +234,32 @@ struct SettingsScreen: View { // MARK: - Previews struct SettingsScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = { - let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com", - deviceID: "AAAAAAAAAAA")))) - return SettingsScreenViewModel(userSession: userSession) - }() + static let viewModel = makeViewModel() + static let bugReportDisabledViewModel = makeViewModel(isBugReportServiceEnabled: false) static var previews: some View { NavigationStack { SettingsScreen(context: viewModel.context) - .snapshotPreferences(expect: viewModel.context.$viewState.map { state in - state.accountSessionsListURL != nil - }) } + .snapshotPreferences(expect: viewModel.context.$viewState.map { state in + state.accountSessionsListURL != nil + }) + .previewDisplayName("Default") + + NavigationStack { + SettingsScreen(context: bugReportDisabledViewModel.context) + } + .snapshotPreferences(expect: bugReportDisabledViewModel.context.$viewState.map { state in + state.accountSessionsListURL != nil + }) + .previewDisplayName("Bug report disabled") + } + + static func makeViewModel(isBugReportServiceEnabled: Bool = true) -> SettingsScreenViewModel { + let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com", + deviceID: "AAAAAAAAAAA")))) + return SettingsScreenViewModel(userSession: userSession, + appSettings: ServiceLocator.shared.settings, + isBugReportServiceEnabled: isBugReportServiceEnabled) } } diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index dd6467259..8f07b25f3 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -123,7 +123,7 @@ struct TimelineViewState: BindableState { var emojiProvider: EmojiProviderProtocol - var mapURLBuilder: MapTilerURLBuilderProtocol + var mapTilerConfiguration: MapTilerConfiguration var bindings: TimelineViewStateBindings } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 3c1cbfa7a..c8a75c07a 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -95,7 +95,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { hideTimelineMedia: appSettings.hideTimelineMedia, pinnedEventIDs: roomProxy.infoPublisher.value.pinnedEventIDs, emojiProvider: emojiProvider, - mapURLBuilder: appSettings.mapTilerConfiguration, + mapTilerConfiguration: appSettings.mapTilerConfiguration, bindings: .init(reactionsCollapsed: [:])), mediaProvider: mediaProvider) diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift index f0ffe47c2..1a9717d12 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift @@ -17,6 +17,7 @@ struct LocationRoomTimelineView: View { .accessibilityElement(children: .ignore) .accessibilityLabel(accessibilityLabel) .onTapGesture { + guard context.viewState.mapTilerConfiguration.isEnabled else { return } context.send(viewAction: .mediaTapped(itemID: timelineItem.id)) } } @@ -30,7 +31,7 @@ struct LocationRoomTimelineView: View { .frame(maxWidth: mapAspectRatio * mapMaxHeight, alignment: .leading) MapLibreStaticMapView(geoURI: geoURI, - mapURLBuilder: context.viewState.mapURLBuilder, + mapURLBuilder: context.viewState.mapTilerConfiguration, mapSize: .init(width: mapAspectRatio * mapMaxHeight, height: mapMaxHeight)) { LocationMarkerView() } diff --git a/ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift b/ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift index d4e0e997d..999b2b853 100644 --- a/ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift +++ b/ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift @@ -9,12 +9,8 @@ import Foundation /// A type that represents how to set up the analytics module in the app. struct AnalyticsConfiguration { - /// Whether or not analytics should be enabled. - let isEnabled: Bool /// The host to use for PostHog analytics. let host: String /// The public key for submitting analytics. let apiKey: String - /// The URL to open with more information about analytics terms. - let termsURL: URL } diff --git a/ElementX/Sources/Services/Analytics/AnalyticsService.swift b/ElementX/Sources/Services/Analytics/AnalyticsService.swift index 5543f6484..fb61d5e25 100644 --- a/ElementX/Sources/Services/Analytics/AnalyticsService.swift +++ b/ElementX/Sources/Services/Analytics/AnalyticsService.swift @@ -39,7 +39,7 @@ class AnalyticsService { /// Whether to show the user the analytics opt in prompt. var shouldShowAnalyticsPrompt: Bool { // Only show the prompt once, and when analytics are enabled in BuildSettings. - appSettings.analyticsConsentState == .unknown && appSettings.analyticsConfiguration.isEnabled + appSettings.analyticsConsentState == .unknown && appSettings.canPromptForAnalytics } var isEnabled: Bool { @@ -65,9 +65,9 @@ class AnalyticsService { /// Starts the analytics client if the user has opted in, otherwise does nothing. func startIfEnabled() { - guard isEnabled, !client.isRunning else { return } + guard isEnabled, !client.isRunning, let configuration = appSettings.analyticsConfiguration else { return } - client.start(analyticsConfiguration: appSettings.analyticsConfiguration) + client.start(analyticsConfiguration: configuration) // Sanity check in case something went wrong. guard client.isRunning else { return } diff --git a/ElementX/Sources/Services/Analytics/PHGPostHogConfiguration.swift b/ElementX/Sources/Services/Analytics/PHGPostHogConfiguration.swift index 5fb829107..4806d8600 100644 --- a/ElementX/Sources/Services/Analytics/PHGPostHogConfiguration.swift +++ b/ElementX/Sources/Services/Analytics/PHGPostHogConfiguration.swift @@ -9,8 +9,6 @@ import PostHog extension PostHogConfig { static func standard(analyticsConfiguration: AnalyticsConfiguration) -> PostHogConfig? { - guard analyticsConfiguration.isEnabled else { return nil } - let postHogConfiguration = PostHogConfig(apiKey: analyticsConfiguration.apiKey, host: analyticsConfiguration.host) // We capture screens manually postHogConfiguration.captureScreenViews = false diff --git a/ElementX/Sources/Services/BugReport/BugReportService.swift b/ElementX/Sources/Services/BugReport/BugReportService.swift index f464a46c2..6531d048f 100644 --- a/ElementX/Sources/Services/BugReport/BugReportService.swift +++ b/ElementX/Sources/Services/BugReport/BugReportService.swift @@ -12,8 +12,8 @@ import Sentry import UIKit class BugReportService: NSObject, BugReportServiceProtocol { - private let baseURL: URL - private let applicationId: String + private let baseURL: URL? + private let applicationID: String private let sdkGitSHA: String private let maxUploadSize: Int private let session: URLSession @@ -23,16 +23,17 @@ class BugReportService: NSObject, BugReportServiceProtocol { private let progressSubject = PassthroughSubject() private var cancellables = Set() + var isEnabled: Bool { baseURL != nil } var lastCrashEventID: String? - init(withBaseURL baseURL: URL, - applicationId: String, + init(baseURL: URL?, + applicationID: String, sdkGitSHA: String, maxUploadSize: Int, session: URLSession = .shared, appHooks: AppHooks) { self.baseURL = baseURL - self.applicationId = applicationId + self.applicationID = applicationID self.sdkGitSHA = sdkGitSHA self.maxUploadSize = maxUploadSize self.session = session @@ -49,6 +50,10 @@ class BugReportService: NSObject, BugReportServiceProtocol { // swiftlint:disable:next cyclomatic_complexity func submitBugReport(_ bugReport: BugReport, progressListener: CurrentValueSubject) async -> Result { + guard let baseURL else { + fatalError("No bug report URL set, the screen should not be shown in this case.") + } + let bugReport = appHooks.bugReportHook.update(bugReport) var params = [ @@ -153,7 +158,7 @@ class BugReportService: NSObject, BugReportServiceProtocol { let (localTime, utcTime) = localAndUTCTime(for: Date()) return [ MultipartFormData(key: "user_agent", type: .text(value: "iOS")), - MultipartFormData(key: "app", type: .text(value: applicationId)), + MultipartFormData(key: "app", type: .text(value: applicationID)), MultipartFormData(key: "version", type: .text(value: InfoPlistReader.main.bundleShortVersionString)), MultipartFormData(key: "build", type: .text(value: InfoPlistReader.main.bundleVersion)), MultipartFormData(key: "sdk_sha", type: .text(value: sdkGitSHA)), diff --git a/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift b/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift index 57f05a295..190fc9b67 100644 --- a/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift +++ b/ElementX/Sources/Services/BugReport/BugReportServiceProtocol.swift @@ -44,6 +44,7 @@ enum BugReportServiceError: LocalizedError { // sourcery: AutoMockable protocol BugReportServiceProtocol: AnyObject { + var isEnabled: Bool { get } var crashedLastRun: Bool { get } var lastCrashEventID: String? { get set } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index ac6836697..8e8f4452f 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -37,7 +37,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate AppSettings.configureWithSuiteName("io.element.elementx.uitests") AppSettings.resetAllSettings() ServiceLocator.shared.register(appSettings: AppSettings()) - ServiceLocator.shared.register(bugReportService: BugReportServiceMock()) + ServiceLocator.shared.register(bugReportService: BugReportServiceMock(.init())) let analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false @@ -124,7 +124,7 @@ class MockScreen: Identifiable { case .authenticationFlow: let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: AuthenticationService.mock, qrCodeLoginService: QRCodeLoginServiceMock(), - bugReportService: BugReportServiceMock(), + bugReportService: BugReportServiceMock(.init()), navigationRootCoordinator: navigationRootCoordinator, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, @@ -228,7 +228,7 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) let userSession = UserSessionMock(.init(clientProxy: clientProxy)) - let coordinator = BugReportScreenCoordinator(parameters: .init(bugReportService: BugReportServiceMock(), + let coordinator = BugReportScreenCoordinator(parameters: .init(bugReportService: BugReportServiceMock(.init()), userSession: userSession, userIndicatorController: nil, screenshot: nil, @@ -557,7 +557,7 @@ class MockScreen: Identifiable { navigationRootCoordinator: navigationRootCoordinator, appLockService: AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings), - bugReportService: BugReportServiceMock(), + bugReportService: BugReportServiceMock(.init()), elementCallService: ElementCallServiceMock(.init()), timelineControllerFactory: TimelineControllerFactoryMock(.init()), appMediator: appMediator, @@ -711,7 +711,7 @@ class MockScreen: Identifiable { navigationRootCoordinator: navigationRootCoordinator, appLockService: AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings), - bugReportService: BugReportServiceMock(), + bugReportService: BugReportServiceMock(.init()), elementCallService: ElementCallServiceMock(.init()), timelineControllerFactory: TimelineControllerFactoryMock(.init(timelineController: timelineController)), appMediator: AppMediatorMock.default, diff --git a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift index c70e3165e..09ce17498 100644 --- a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift +++ b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift @@ -17,7 +17,7 @@ class UnitTestsAppCoordinator: AppCoordinatorProtocol { AppSettings.configureWithSuiteName("io.element.elementx.unittests") AppSettings.resetAllSettings() ServiceLocator.shared.register(appSettings: AppSettings()) - ServiceLocator.shared.register(bugReportService: BugReportServiceMock()) + ServiceLocator.shared.register(bugReportService: BugReportServiceMock(.init())) let analyticsClient = AnalyticsClientMock() analyticsClient.isRunning = false diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPad-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPad-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPad-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPad-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPad-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPad-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPhone-16-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPhone-16-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPhone-16-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPhone-16-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.iPhone-16-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.Default-iPhone-16-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-en-GB.png new file mode 100644 index 000000000..ffabf0636 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6fcab1ccc72812d59a6b927c7731b7c1cae82b457a86ed6530cf610e9ae55f1 +size 509252 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-pseudo.png new file mode 100644 index 000000000..d073b75d5 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:725a62a99cc8483ecb291544dd58028b9e967e8ca3c63a6b64a8cfc1def27800 +size 529773 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-en-GB.png new file mode 100644 index 000000000..01ee25ed8 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae449487cd4e9b77c360a17f57e117a7ec3048a88f33d53f72fbcc22e6c666e4 +size 316319 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-pseudo.png new file mode 100644 index 000000000..a02dd981d --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/analyticsPromptScreen.No-terms-iPhone-16-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c87868f81842fdeeac6f51a73334b1cc1f9c48004256f9e9a50dbaa507c5092b +size 332515 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-en-GB.png new file mode 100644 index 000000000..77abb2992 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:816f085c82fc0866cc1052e8b16bc6cb035d5f69cdc8388dd44d8a5ced97b34e +size 109090 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-pseudo.png new file mode 100644 index 000000000..1c004a5c3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba61a003238a0fb7b3188dca3f5735c386c9d2bd6488ae15d31376d3b1899a73 +size 126888 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-en-GB.png new file mode 100644 index 000000000..83bd813af --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7b8b73c30044017dc8db3f5b540011fb9e502d68beabfae0f85daede922f342 +size 521551 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-pseudo.png new file mode 100644 index 000000000..d886a6b5c --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Bug-report-disabled-iPhone-16-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3074c78aa668a0b9f194e85bbd41af06985c831e6680f4d7bc8c131f0acdb185 +size 544472 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPad-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPad-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-16-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPhone-16-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-16-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-16-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.iPhone-16-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-16-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-en-GB.png new file mode 100644 index 000000000..0171afe5e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82aceb5956ac84373d141855acaa16a962034001d2b4a01c4bd30a347505e2f8 +size 160152 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-pseudo.png new file mode 100644 index 000000000..83e424484 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ce61dcd6449607c67a4aa12d96140e1159afce33481351a16e7af9f90c5e932 +size 166763 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-en-GB.png new file mode 100644 index 000000000..48ee1be07 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:967c85e91f106b89c44f17facb9ac369bbca2e19e33b1cd6875108aba2fd5b49 +size 106646 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-pseudo.png new file mode 100644 index 000000000..3a5191c29 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Bug-report-disabled-iPhone-16-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eab5bec1eb422c8918f0e7c634d2b1cd3b1190dc001547db99159395241fea3e +size 123586 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPad-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPad-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPad-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPad-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPad-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPad-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPhone-16-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPhone-16-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPhone-16-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPhone-16-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.iPhone-16-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/settingsScreen.Default-iPhone-16-pseudo.png diff --git a/UnitTests/Sources/AnalyticsTests.swift b/UnitTests/Sources/AnalyticsTests.swift index 784bce90b..9d78706e3 100644 --- a/UnitTests/Sources/AnalyticsTests.swift +++ b/UnitTests/Sources/AnalyticsTests.swift @@ -155,11 +155,11 @@ class AnalyticsTests: XCTestCase { XCTAssertEqual(client.pendingUserProperties?.numSpaces, 5, "The number of spaces should have been updated.") } - func testSendingUserProperties() { + func testSendingUserProperties() throws { // Given a client with user properties set let client = PostHogAnalyticsClient(posthogFactory: MockPostHogFactory(mock: posthogMock)) - client.start(analyticsConfiguration: appSettings.analyticsConfiguration) + try client.start(analyticsConfiguration: XCTUnwrap(appSettings.analyticsConfiguration)) client.updateUserProperties(AnalyticsEvent.UserProperties(allChatsActiveFilter: nil, ftueUseCaseSelection: .PersonalMessaging, numFavouriteRooms: nil, @@ -204,10 +204,10 @@ class AnalyticsTests: XCTestCase { XCTAssertTrue(ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt) } - func testSendingAndUpdatingSuperProperties() { + func testSendingAndUpdatingSuperProperties() throws { // Given a client with user properties set let client = PostHogAnalyticsClient(posthogFactory: MockPostHogFactory(mock: posthogMock)) - client.start(analyticsConfiguration: appSettings.analyticsConfiguration) + try client.start(analyticsConfiguration: XCTUnwrap(appSettings.analyticsConfiguration)) client.updateSuperProperties( AnalyticsEvent.SuperProperties(appPlatform: .EXI, @@ -264,7 +264,7 @@ class AnalyticsTests: XCTestCase { XCTAssertEqual(capturedEvent2?.properties?["cryptoSDKVersion"] as? String, "001") } - func testShouldNotReportIfNotStarted() { + func testShouldNotReportIfNotStarted() throws { // Given a client with user properties set let client = PostHogAnalyticsClient(posthogFactory: MockPostHogFactory(mock: posthogMock)) @@ -291,7 +291,7 @@ class AnalyticsTests: XCTestCase { XCTAssertEqual(posthogMock.capturePropertiesUserPropertiesCalled, false) // start now - client.start(analyticsConfiguration: appSettings.analyticsConfiguration) + try client.start(analyticsConfiguration: XCTUnwrap(appSettings.analyticsConfiguration)) XCTAssertEqual(posthogMock.optInCalled, true) client.capture(someEvent) diff --git a/UnitTests/Sources/BugReportServiceTests.swift b/UnitTests/Sources/BugReportServiceTests.swift index 81b3a5fba..9cf141bc4 100644 --- a/UnitTests/Sources/BugReportServiceTests.swift +++ b/UnitTests/Sources/BugReportServiceTests.swift @@ -41,18 +41,30 @@ class BugReportServiceTests: XCTestCase { } func testInitialStateWithRealService() throws { - let service = BugReportService(withBaseURL: "https://www.example.com", - applicationId: "mock_app_id", + let service = BugReportService(baseURL: "https://www.example.com", + applicationID: "mock_app_id", sdkGitSHA: "1234", maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize, session: .mock, appHooks: AppHooks()) + XCTAssertTrue(service.isEnabled) + XCTAssertFalse(service.crashedLastRun) + } + + func testInitialStateWithRealServiceAndNoURL() throws { + let service = BugReportService(baseURL: nil, + applicationID: "mock_app_id", + sdkGitSHA: "1234", + maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize, + session: .mock, + appHooks: AppHooks()) + XCTAssertFalse(service.isEnabled) XCTAssertFalse(service.crashedLastRun) } @MainActor func testSubmitBugReportWithRealService() async throws { - let service = BugReportService(withBaseURL: "https://www.example.com", - applicationId: "mock_app_id", + let service = BugReportService(baseURL: "https://www.example.com", + applicationID: "mock_app_id", sdkGitSHA: "1234", maxUploadSize: ServiceLocator.shared.settings.bugReportMaxUploadSize, session: .mock, diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index b786e2eb1..e8fe76233 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -100,6 +100,7 @@ class ComposerToolbarViewModelTests: XCTestCase { completionSuggestionService: mockCompletionSuggestionService, mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) @@ -684,6 +685,7 @@ class ComposerToolbarViewModelTests: XCTestCase { completionSuggestionService: mockCompletionSuggestionService, mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) @@ -727,6 +729,7 @@ class ComposerToolbarViewModelTests: XCTestCase { completionSuggestionService: mockCompletionSuggestionService, mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) @@ -760,6 +763,7 @@ class ComposerToolbarViewModelTests: XCTestCase { completionSuggestionService: mockCompletionSuggestionService, mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) @@ -794,6 +798,7 @@ class ComposerToolbarViewModelTests: XCTestCase { completionSuggestionService: completionSuggestionServiceMock, mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) viewModel.context.composerFormattingEnabled = true diff --git a/UnitTests/Sources/MapTilerURLBuilderTests.swift b/UnitTests/Sources/MapTilerURLBuilderTests.swift index fa6453059..146459135 100644 --- a/UnitTests/Sources/MapTilerURLBuilderTests.swift +++ b/UnitTests/Sources/MapTilerURLBuilderTests.swift @@ -51,4 +51,24 @@ final class MapTilerURLBuilderTests: XCTestCase { let expectedURL: URL = "http://www.foo.com/dea61faf-292b-4774-9660-58fcef89a7f3/style.json?key=some_key" XCTAssertEqual(url, expectedURL) } + + func testNilAPIKey() { + let configuration = MapTilerConfiguration(baseURL: Self.baseURL, + apiKey: nil, + lightStyleID: Self.lightStyleID, + darkStyleID: Self.darkStyleID) + XCTAssertFalse(configuration.isEnabled) + + builder = configuration + + let staticMapURL = builder.staticMapURL(for: .dark, + coordinates: .init(latitude: 1, longitude: 2), + zoomLevel: 5, + size: .init(width: 300, height: 200), + attribution: .topLeft) + XCTAssertNil(staticMapURL) + + let dynamicMapURL = builder.dynamicMapURL(for: .light) + XCTAssertNil(dynamicMapURL) + } } diff --git a/UnitTests/Sources/SettingsViewModelTests.swift b/UnitTests/Sources/SettingsViewModelTests.swift index 96bb853ed..69633a991 100644 --- a/UnitTests/Sources/SettingsViewModelTests.swift +++ b/UnitTests/Sources/SettingsViewModelTests.swift @@ -19,7 +19,9 @@ class SettingsScreenViewModelTests: XCTestCase { @MainActor override func setUpWithError() throws { cancellables.removeAll() let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "")))) - viewModel = SettingsScreenViewModel(userSession: userSession) + viewModel = SettingsScreenViewModel(userSession: userSession, + appSettings: ServiceLocator.shared.settings, + isBugReportServiceEnabled: true) context = viewModel.context } diff --git a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift index 96272b876..6b93dd9c3 100644 --- a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift +++ b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift @@ -36,7 +36,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase { userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), navigationRootCoordinator: navigationRootCoordinator, appLockService: AppLockServiceMock(), - bugReportService: BugReportServiceMock(), + bugReportService: BugReportServiceMock(.init()), elementCallService: ElementCallServiceMock(.init()), timelineControllerFactory: timelineControllerFactory, appMediator: AppMediatorMock.default,