mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
fix: remove rlement endpoints and links from the HealthChat
* fix(healthchat): switch off analytics, crash and bug reporting endpoints Upstream ships *.localhost placeholders in Secrets.swift and marks the file assume-unchanged; with analytics on, the Element Call widget was handed Element's own PostHog and Sentry. nil hides the analytics prompt, the Analytics and Report a problem entries until HealthChat runs its own services. * feat(healthchat): hide the Labs entry in settings * fix(healthchat): point Copyright and the analytics terms at healthchat.ch * docs(healthchat): add the 26.11.1 release notes * feat(healthchat): drop the Element help links * feat(healthchat): switch off location sharing * test(healthchat): skip the analytics suite without PostHog
This commit is contained in:
@@ -43,7 +43,13 @@ Upstream files the fork edits — conflicts live here every time:
|
||||
migration contract"), `AppHooks/UserHooks.swift` (two HealthChat hook fields),
|
||||
`AppSettings.swift` (VoIP push properties for the NSE), `ElementCallService.swift` (PushKit
|
||||
token, expiration parsing), `NSE/Sources/NotificationHandler.swift` (VoIP push to Sygnal),
|
||||
`UserSessionFlowCoordinator` (Members tab), `SettingsScreen*` (account status section),
|
||||
`UserSessionFlowCoordinator` (Members tab), `SettingsScreen*` (account status section, Labs
|
||||
gate), the five help URLs are `URL?` (`AppSettings`, `SessionVerificationScreen*`,
|
||||
`IdentityConfirmationScreen*`, `SecureBackupScreen*`, `RoomScreenModels` + `RoomScreenFooterView`,
|
||||
`SecurityAndPrivacyScreenModels`, `TimelineViewModel` alert — nil hides the "Learn more" link), `UnitTests/Sources/AnalyticsTests.swift` (suite skipped without PostHog secrets),
|
||||
`Components/Secrets/Secrets.swift` (analytics/sentry/rageshake nil, hand-edited; upstream's setup
|
||||
marks it `assume-unchanged`, so run `git update-index --no-assume-unchanged` on it before an
|
||||
edit shows up in `git status` or a commit),
|
||||
`RoomMemberDetailsScreen*` + `UserProfileScreen*` (extended profile section), the five fork-gated
|
||||
CI workflows, `.gitignore`, `project.pbxproj` (never resolve by hand — `xcodegen`).
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
nonisolated enum Secrets {
|
||||
static let sentryDSN: String? = "https://username@sentry.localhost/project_id"
|
||||
static let sentryRustDSN: String? = "https://username@sentry.localhost/project_id"
|
||||
static let postHogHost: String? = "https://posthog.localhost"
|
||||
static let postHogAPIKey: String? = "your_key"
|
||||
static let rageshakeURL: String? = "https://rageshake.localhost/submit"
|
||||
static let sentryDSN: String? = nil
|
||||
static let sentryRustDSN: String? = nil
|
||||
static let postHogHost: String? = nil
|
||||
static let postHogAPIKey: String? = nil
|
||||
static let rageshakeURL: String? = nil
|
||||
static let mapLibreAPIKey: String? = "your_key"
|
||||
|
||||
}
|
||||
@@ -12,7 +12,8 @@ import Foundation
|
||||
/// - `accountProviders` / `allowOtherAccountProviders` lock every user to `healthchat.ch` (the server name;
|
||||
/// `.well-known` delegates to `matrix.healthchat.ch`) and hide the change-server / create-account UI.
|
||||
/// - `oAuthRedirectURL` uses the app's own URL scheme so the OIDC/MAS callback needs no associated domain.
|
||||
/// - Legacy `element.io` help/encryption/copyright URLs are passed through unchanged for now.
|
||||
/// - Legal links point at healthchat.ch. The encryption/verification/backup help links still go to
|
||||
/// Element's help pages because no HealthChat equivalents exist yet.
|
||||
struct HealthChatAppSettingsHook: AppSettingsHookProtocol {
|
||||
func configure(_ appSettings: AppSettings) -> AppSettings {
|
||||
appSettings.override(accountProviders: [.generic("healthchat.ch")],
|
||||
@@ -24,19 +25,25 @@ struct HealthChatAppSettingsHook: AppSettingsHookProtocol {
|
||||
oAuthClientURIPath: nil,
|
||||
websiteURL: "https://healthchat.ch",
|
||||
logoURL: "https://app.healthchat.ch/themes/element/img/logos/element-logo.png",
|
||||
copyrightURL: appSettings.copyrightURL,
|
||||
copyrightURL: "https://www.healthchat.ch/impressum",
|
||||
acceptableUseURL: "https://www.healthchat.ch/datenschutz",
|
||||
privacyURL: "https://www.healthchat.ch/datenschutz",
|
||||
encryptionURL: appSettings.encryptionURL,
|
||||
deviceVerificationURL: appSettings.deviceVerificationURL,
|
||||
chatBackupDetailsURL: appSettings.chatBackupDetailsURL,
|
||||
identityPinningViolationDetailsURL: appSettings.identityPinningViolationDetailsURL,
|
||||
historySharingDetailsURL: appSettings.historySharingDetailsURL,
|
||||
encryptionURL: nil,
|
||||
deviceVerificationURL: nil,
|
||||
chatBackupDetailsURL: nil,
|
||||
identityPinningViolationDetailsURL: nil,
|
||||
historySharingDetailsURL: nil,
|
||||
elementWebHosts: ["app.healthchat.ch"],
|
||||
accountProvisioningHost: appSettings.accountProvisioningHost,
|
||||
bugReportApplicationID: appSettings.bugReportApplicationID,
|
||||
analyticsTermsURL: appSettings.analyticsTermsURL,
|
||||
mapTilerConfiguration: AppSettings.bundledMapTilerConfiguration)
|
||||
bugReportApplicationID: "healthchat-ios",
|
||||
analyticsTermsURL: "https://www.healthchat.ch/datenschutz",
|
||||
mapTilerConfiguration: Self.mapTilerConfiguration)
|
||||
return appSettings
|
||||
}
|
||||
|
||||
/// No MapTiler account yet: a nil key hides location sharing and shows received locations as a placeholder.
|
||||
private static let mapTilerConfiguration = MapTilerConfiguration(baseURL: AppSettings.bundledMapTilerConfiguration.baseURL,
|
||||
apiKey: nil,
|
||||
lightStyleID: AppSettings.bundledMapTilerConfiguration.lightStyleID,
|
||||
darkStyleID: AppSettings.bundledMapTilerConfiguration.darkStyleID)
|
||||
}
|
||||
|
||||
@@ -28,5 +28,10 @@ final class HealthChatSettingsScreenHook: SettingsScreenHookProtocol {
|
||||
func refreshAccountStatus() {
|
||||
statusLoader.load()
|
||||
}
|
||||
|
||||
// Upstream's experiments are not supported by the HealthChat server setup and never reviewed for it.
|
||||
nonisolated var showsLabs: Bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,9 @@ nonisolated protocol SettingsScreenHookProtocol: Sendable {
|
||||
|
||||
/// Called whenever the settings screen appears, so the section reflects what the user just did.
|
||||
@MainActor func refreshAccountStatus()
|
||||
|
||||
/// Whether the Labs entry (experimental upstream features) is offered at all.
|
||||
var showsLabs: Bool { get }
|
||||
}
|
||||
|
||||
struct DefaultSettingsScreenHook: SettingsScreenHookProtocol {
|
||||
@@ -23,4 +26,8 @@ struct DefaultSettingsScreenHook: SettingsScreenHookProtocol {
|
||||
}
|
||||
|
||||
func refreshAccountStatus() { }
|
||||
|
||||
var showsLabs: Bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,11 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
copyrightURL: URL,
|
||||
acceptableUseURL: URL,
|
||||
privacyURL: URL,
|
||||
encryptionURL: URL,
|
||||
deviceVerificationURL: URL,
|
||||
chatBackupDetailsURL: URL,
|
||||
identityPinningViolationDetailsURL: URL,
|
||||
historySharingDetailsURL: URL,
|
||||
encryptionURL: URL?,
|
||||
deviceVerificationURL: URL?,
|
||||
chatBackupDetailsURL: URL?,
|
||||
identityPinningViolationDetailsURL: URL?,
|
||||
historySharingDetailsURL: URL?,
|
||||
elementWebHosts: [String],
|
||||
accountProvisioningHost: String,
|
||||
bugReportApplicationID: String,
|
||||
@@ -145,15 +145,15 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
/// A URL that contains the app's Privacy Policy.
|
||||
private(set) var privacyURL: URL = "https://element.io/privacy"
|
||||
/// A URL where users can go read more about encryption in general.
|
||||
private(set) var encryptionURL: URL = "https://element.io/help#encryption"
|
||||
private(set) var encryptionURL: URL? = "https://element.io/help#encryption"
|
||||
/// A URL where users can go read more about device verification..
|
||||
private(set) var deviceVerificationURL: URL = "https://element.io/help#encryption-device-verification"
|
||||
private(set) var deviceVerificationURL: URL? = "https://element.io/help#encryption-device-verification"
|
||||
/// A URL where users can go read more about the chat backup.
|
||||
private(set) var chatBackupDetailsURL: URL = "https://element.io/help#encryption5"
|
||||
private(set) var chatBackupDetailsURL: URL? = "https://element.io/help#encryption5"
|
||||
/// A URL where users can go read more about identity pinning violations
|
||||
private(set) var identityPinningViolationDetailsURL: URL = "https://element.io/help#encryption18"
|
||||
private(set) var identityPinningViolationDetailsURL: URL? = "https://element.io/help#encryption18"
|
||||
/// A URL describing how history sharing works
|
||||
private(set) var historySharingDetailsURL: URL = "https://element.io/en/help#e2ee-history-sharing"
|
||||
private(set) var historySharingDetailsURL: URL? = "https://element.io/en/help#e2ee-history-sharing"
|
||||
|
||||
/// Any domains that Element web may be hosted on - used for handling links.
|
||||
private(set) var elementWebHosts = ["app.element.io", "staging.element.io", "develop.element.io"]
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ struct IdentityConfirmationScreenViewState: BindableState {
|
||||
}
|
||||
|
||||
var availableActions: [AvailableActions]?
|
||||
let learnMoreURL: URL
|
||||
let learnMoreURL: URL?
|
||||
|
||||
var bindings = IdentityConfirmationScreenBindings()
|
||||
}
|
||||
|
||||
+6
-4
@@ -53,11 +53,13 @@ struct IdentityConfirmationScreen: View {
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
|
||||
Button(L10n.actionLearnMore) {
|
||||
UIApplication.shared.open(context.viewState.learnMoreURL)
|
||||
if let learnMoreURL = context.viewState.learnMoreURL {
|
||||
Button(L10n.actionLearnMore) {
|
||||
UIApplication.shared.open(learnMoreURL)
|
||||
}
|
||||
.buttonStyle(.compound(.tertiary, size: .small))
|
||||
.padding(.top, 16)
|
||||
}
|
||||
.buttonStyle(.compound(.tertiary, size: .small))
|
||||
.padding(.top, 16)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ enum SessionVerificationScreenViewAction {
|
||||
|
||||
struct SessionVerificationScreenViewState: BindableState {
|
||||
let flow: SessionVerificationScreenFlow
|
||||
let learnMoreURL: URL
|
||||
let learnMoreURL: URL?
|
||||
|
||||
var verificationState: SessionVerificationScreenStateMachine.State
|
||||
|
||||
|
||||
+5
-3
@@ -92,10 +92,12 @@ struct SessionVerificationScreen: View {
|
||||
isUserVerification: true,
|
||||
mediaProvider: context.mediaProvider)
|
||||
case .userInitiator:
|
||||
Button(L10n.actionLearnMore) {
|
||||
UIApplication.shared.open(context.viewState.learnMoreURL)
|
||||
if let learnMoreURL = context.viewState.learnMoreURL {
|
||||
Button(L10n.actionLearnMore) {
|
||||
UIApplication.shared.open(learnMoreURL)
|
||||
}
|
||||
.buttonStyle(.compound(.tertiary, size: .small))
|
||||
}
|
||||
.buttonStyle(.compound(.tertiary, size: .small))
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ enum RoomScreenFooterViewAction {
|
||||
}
|
||||
|
||||
enum RoomScreenFooterViewDetails {
|
||||
case pinViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL)
|
||||
case verificationViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL)
|
||||
case pinViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL?)
|
||||
case verificationViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL?)
|
||||
}
|
||||
|
||||
enum PinnedEventsBannerState: Equatable {
|
||||
|
||||
@@ -56,7 +56,7 @@ struct RoomScreenFooterView: View {
|
||||
}
|
||||
|
||||
private func pinViolation(member: RoomMemberProxyProtocol,
|
||||
learnMoreURL: URL) -> some View {
|
||||
learnMoreURL: URL?) -> some View {
|
||||
VStack(spacing: 16) {
|
||||
HStack(spacing: 16) {
|
||||
LoadableAvatarImage(url: member.avatarURL,
|
||||
@@ -86,7 +86,7 @@ struct RoomScreenFooterView: View {
|
||||
}
|
||||
|
||||
private func verificationViolation(member: RoomMemberProxyProtocol,
|
||||
learnMoreURL: URL) -> some View {
|
||||
learnMoreURL: URL?) -> some View {
|
||||
VStack(spacing: 16) {
|
||||
HStack(spacing: 16) {
|
||||
LoadableAvatarImage(url: member.avatarURL,
|
||||
@@ -115,7 +115,7 @@ struct RoomScreenFooterView: View {
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
private func pinViolationDescriptionWithLearnMoreLink(displayName: String?, userID: String, url: URL) -> AttributedString {
|
||||
private func pinViolationDescriptionWithLearnMoreLink(displayName: String?, userID: String, url: URL?) -> AttributedString {
|
||||
let userIDPlaceholder = "{mxid}"
|
||||
let linkPlaceholder = "{link}"
|
||||
let displayName = displayName ?? fallbackDisplayName(userID)
|
||||
@@ -125,14 +125,14 @@ struct RoomScreenFooterView: View {
|
||||
userIDString.bold()
|
||||
description.replace(userIDPlaceholder, with: userIDString)
|
||||
|
||||
var linkString = AttributedString(L10n.actionLearnMore)
|
||||
var linkString = AttributedString(url == nil ? "" : L10n.actionLearnMore)
|
||||
linkString.link = url
|
||||
linkString.bold()
|
||||
description.replace(linkPlaceholder, with: linkString)
|
||||
return description
|
||||
}
|
||||
|
||||
private func verificationViolationDescriptionWithLearnMoreLink(displayName: String?, userID: String, url: URL) -> AttributedString {
|
||||
private func verificationViolationDescriptionWithLearnMoreLink(displayName: String?, userID: String, url: URL?) -> AttributedString {
|
||||
let userIDPlaceholder = "{mxid}"
|
||||
let linkPlaceholder = "{link}"
|
||||
let displayName = displayName ?? fallbackDisplayName(userID)
|
||||
@@ -142,7 +142,7 @@ struct RoomScreenFooterView: View {
|
||||
userIDString.bold()
|
||||
description.replace(userIDPlaceholder, with: userIDString)
|
||||
|
||||
var linkString = AttributedString(L10n.actionLearnMore)
|
||||
var linkString = AttributedString(url == nil ? "" : L10n.actionLearnMore)
|
||||
linkString.link = url
|
||||
linkString.bold()
|
||||
description.replace(linkPlaceholder, with: linkString)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ enum SecureBackupScreenViewModelAction {
|
||||
}
|
||||
|
||||
struct SecureBackupScreenViewState: BindableState {
|
||||
let chatBackupDetailsURL: URL
|
||||
let chatBackupDetailsURL: URL?
|
||||
var recoveryState = SecureBackupRecoveryState.unknown
|
||||
var keyBackupState = SecureBackupKeyBackupState.unknown
|
||||
var bindings: SecureBackupScreenViewStateBindings
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup
|
||||
|
||||
init(secureBackupController: SecureBackupControllerProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
chatBackupDetailsURL: URL) {
|
||||
chatBackupDetailsURL: URL?) {
|
||||
self.secureBackupController = secureBackupController
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
|
||||
+6
-1
@@ -64,10 +64,15 @@ struct SecureBackupScreen: View {
|
||||
}
|
||||
|
||||
private var keyBackupDescriptionWithLearnMoreLink: AttributedString {
|
||||
guard let chatBackupDetailsURL = context.viewState.chatBackupDetailsURL else {
|
||||
// The link is its own sentence, so the full stop goes with it.
|
||||
return AttributedString(L10n.screenChatBackupKeyBackupDescription("").replacing(/\s*\.\s*$/, with: ""))
|
||||
}
|
||||
|
||||
let linkPlaceholder = "{link}"
|
||||
var description = AttributedString(L10n.screenChatBackupKeyBackupDescription(linkPlaceholder))
|
||||
var linkString = AttributedString(L10n.actionLearnMore)
|
||||
linkString.link = context.viewState.chatBackupDetailsURL
|
||||
linkString.link = chatBackupDetailsURL
|
||||
linkString.bold()
|
||||
description.replace(linkPlaceholder, with: linkString)
|
||||
return description
|
||||
|
||||
+3
-3
@@ -162,7 +162,7 @@ struct SecurityAndPrivacyScreenViewState: BindableState {
|
||||
historyVisibility: SecurityAndPrivacyHistoryVisibility,
|
||||
isSpace: Bool,
|
||||
isKnockingEnabled: Bool,
|
||||
historySharingDetailsURL: URL) {
|
||||
historySharingDetailsURL: URL?) {
|
||||
self.isKnockingEnabled = isKnockingEnabled
|
||||
self.isSpace = isSpace
|
||||
|
||||
@@ -269,7 +269,7 @@ struct SecurityAndPrivacyScreenStrings {
|
||||
let accessSectionFooterString: AttributedString
|
||||
let historySectionFooterString: AttributedString
|
||||
|
||||
init(historySharingDetailsURL: URL) {
|
||||
init(historySharingDetailsURL: URL?) {
|
||||
let linkPlaceholder = "{link}"
|
||||
|
||||
var accessFooterString = AttributedString(L10n.screenSecurityAndPrivacyRoomAccessFooter(linkPlaceholder))
|
||||
@@ -280,7 +280,7 @@ struct SecurityAndPrivacyScreenStrings {
|
||||
accessSectionFooterString = accessFooterString
|
||||
|
||||
var historyFooterString = AttributedString(L10n.screenSecurityAndPrivacyRoomHistorySectionFooter(linkPlaceholder))
|
||||
var historyLinkString = AttributedString(L10n.actionLearnMore)
|
||||
var historyLinkString = AttributedString(historySharingDetailsURL == nil ? "" : L10n.actionLearnMore)
|
||||
historyLinkString.link = historySharingDetailsURL
|
||||
historyLinkString.bold()
|
||||
historyFooterString.replace(linkPlaceholder, with: historyLinkString)
|
||||
|
||||
@@ -211,11 +211,13 @@ struct SettingsScreen: View {
|
||||
.accessibilityIdentifier(A11yIdentifiers.settingsScreen.analytics)
|
||||
}
|
||||
|
||||
ListRow(label: .default(title: L10n.screenAdvancedSettingsLabs,
|
||||
icon: \.labs),
|
||||
kind: .navigationLink {
|
||||
context.send(viewAction: .labs)
|
||||
})
|
||||
if context.viewState.settingsScreenHook.showsLabs {
|
||||
ListRow(label: .default(title: L10n.screenAdvancedSettingsLabs,
|
||||
icon: \.labs),
|
||||
kind: .navigationLink {
|
||||
context.send(viewAction: .labs)
|
||||
})
|
||||
}
|
||||
|
||||
ListRow(label: .default(title: L10n.commonAbout,
|
||||
icon: \.info),
|
||||
|
||||
@@ -1143,9 +1143,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
state.bindings.alertInfo = .init(id: type,
|
||||
title: message,
|
||||
primaryButton: .init(title: L10n.actionOk, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionLearnMore) { [weak self] in
|
||||
guard let self else { return }
|
||||
appMediator.open(userSettings.app.historySharingDetailsURL)
|
||||
secondaryButton: userSettings.app.historySharingDetailsURL.map { url in
|
||||
.init(title: L10n.actionLearnMore) { [weak self] in self?.appMediator.open(url) }
|
||||
})
|
||||
case .inviteAgain:
|
||||
state.bindings.alertInfo = .init(id: .inviteAgain,
|
||||
|
||||
@@ -9,6 +9,27 @@ matches `## Changes in <version>` exactly.
|
||||
|
||||
Upstream's `CHANGES.md` tracks element-hq's own releases and must not be edited here.
|
||||
|
||||
## Changes in 26.11.1 (2026-10-19)
|
||||
|
||||
🙌 Improvements
|
||||
|
||||
- Copyright in "About" now opens the HealthChat Impressum.
|
||||
- The Labs entry with Element's experimental features is no longer shown.
|
||||
- Analytics and crash reporting are switched off entirely: the "Analytics" settings and the
|
||||
onboarding prompt are gone, and calls no longer send usage data to Element.
|
||||
- "Report a problem" is hidden until HealthChat runs its own report server.
|
||||
- The "Learn more" links on identity confirmation, device verification, chat backup, identity
|
||||
changes and history sharing are gone; they led to Element's documentation.
|
||||
- Location sharing is switched off: the attachment menu no longer offers it, and received
|
||||
locations show a placeholder instead of a map that could not load.
|
||||
|
||||
⚠️ Known issues
|
||||
|
||||
- Incoming calls are labelled as video calls on the call screen; voice calls still connect
|
||||
as voice calls.
|
||||
- An "Incoming call" notification is shown next to the call screen, and a few notifications
|
||||
may show generic text, while Apple reviews the notification filtering entitlement.
|
||||
|
||||
## Changes in 26.11.0 (2026-10-19)
|
||||
|
||||
✨ Features
|
||||
|
||||
@@ -11,6 +11,7 @@ import AnalyticsEvents
|
||||
import PostHog
|
||||
import Testing
|
||||
|
||||
@Suite(.enabled(if: Secrets.postHogHost != nil && Secrets.postHogAPIKey != nil, "HealthChat ships without a PostHog configuration."))
|
||||
@MainActor
|
||||
final class AnalyticsTests {
|
||||
private let appSettings: AppSettings
|
||||
|
||||
@@ -104,9 +104,12 @@ final class BugReportServiceTests {
|
||||
@Test
|
||||
@MainActor
|
||||
func configurations() async throws {
|
||||
guard case let .url(initialURL) = userSettings.app.bugReportRageshakeURL.publisher.value else {
|
||||
Issue.record("Unexpected initial configuration.")
|
||||
return
|
||||
// HealthChat ships without a rageshake server (Secrets.rageshakeURL is nil), so the default
|
||||
// configuration is `.disabled`; the transitions below are exercised from remote values instead.
|
||||
let initialURL: URL? = if case let .url(url) = userSettings.app.bugReportRageshakeURL.publisher.value {
|
||||
url
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
|
||||
let service = BugReportService(rageshakeURLPublisher: userSettings.app.bugReportRageshakeURL.publisher,
|
||||
@@ -114,7 +117,7 @@ final class BugReportServiceTests {
|
||||
sdkGitSHA: "1234",
|
||||
session: .mock,
|
||||
appHooks: AppHooks())
|
||||
#expect(service.isEnabled)
|
||||
#expect(service.isEnabled == (initialURL != nil))
|
||||
|
||||
userSettings.app.bugReportRageshakeURL.applyRemoteValue(.disabled)
|
||||
#expect(!service.isEnabled)
|
||||
@@ -137,6 +140,10 @@ final class BugReportServiceTests {
|
||||
#expect(customConfigurationResponse.reportURL == "https://bugs.server.net/123")
|
||||
|
||||
userSettings.app.bugReportRageshakeURL.reset()
|
||||
guard let initialURL else {
|
||||
#expect(!service.isEnabled)
|
||||
return
|
||||
}
|
||||
#expect(service.isEnabled)
|
||||
|
||||
let defaultConfigurationResponse = try await service.submitBugReport(bugReport, progressListener: progressSubject).get()
|
||||
|
||||
Reference in New Issue
Block a user