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:
Sedat-Qaja
2026-10-19 14:40:20 +02:00
committed by GitHub
parent 7b07b51a10
commit 3b7ed0a48b
21 changed files with 125 additions and 61 deletions
@@ -24,7 +24,7 @@ struct IdentityConfirmationScreenViewState: BindableState {
}
var availableActions: [AvailableActions]?
let learnMoreURL: URL
let learnMoreURL: URL?
var bindings = IdentityConfirmationScreenBindings()
}
@@ -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)
}
}
@@ -26,7 +26,7 @@ enum SessionVerificationScreenViewAction {
struct SessionVerificationScreenViewState: BindableState {
let flow: SessionVerificationScreenFlow
let learnMoreURL: URL
let learnMoreURL: URL?
var verificationState: SessionVerificationScreenStateMachine.State
@@ -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)
@@ -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
@@ -22,7 +22,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup
init(secureBackupController: SecureBackupControllerProtocol,
userIndicatorController: UserIndicatorControllerProtocol,
chatBackupDetailsURL: URL) {
chatBackupDetailsURL: URL?) {
self.secureBackupController = secureBackupController
self.userIndicatorController = userIndicatorController
@@ -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
@@ -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,