Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,3 @@
"soft_logout_clear_data_submit" = "Clear all data";
"soft_logout_clear_data_dialog_title" = "Clear data";
"soft_logout_clear_data_dialog_content" = "Clear all data currently stored on this device?\nSign in again to access your account data and messages.";

// MARK: - Shared history visibility

"crypto_history_visible" = "Messages you send will be shared with new members invited to this room. %1$s";
11 changes: 7 additions & 4 deletions ElementX/Sources/Application/Settings/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ final class AppSettings {
case seenInvites
case hasSeenSpacesAnnouncement
case hasSeenNewSoundBanner
case hasSeenHistoryVisibleBannerRooms
case appLockNumberOfPINAttempts
case appLockNumberOfBiometricAttempts
case timelineStyle
Expand All @@ -54,8 +55,6 @@ final class AppSettings {

case elementCallBaseURLOverride

case acknowledgedHistoryVisibleRooms

// Feature flags
case publicSearchEnabled
case fuzzyRoomListSearchEnabled
Expand Down Expand Up @@ -127,6 +126,7 @@ final class AppSettings {
deviceVerificationURL: URL,
chatBackupDetailsURL: URL,
identityPinningViolationDetailsURL: URL,
historyVisibleDetailsURL: URL,
elementWebHosts: [String],
accountProvisioningHost: String,
bugReportApplicationID: String,
Expand All @@ -146,6 +146,7 @@ final class AppSettings {
self.deviceVerificationURL = deviceVerificationURL
self.chatBackupDetailsURL = chatBackupDetailsURL
self.identityPinningViolationDetailsURL = identityPinningViolationDetailsURL
self.historyVisibleDetailsURL = historyVisibleDetailsURL
self.elementWebHosts = elementWebHosts
self.accountProvisioningHost = accountProvisioningHost
self.bugReportApplicationID = bugReportApplicationID
Expand Down Expand Up @@ -174,8 +175,8 @@ final class AppSettings {
var hasSeenNewSoundBanner

/// The Set of room identifiers that the user has acknowledged have visible history.
@UserPreference(key: UserDefaultsKeys.acknowledgedHistoryVisibleRooms, defaultValue: [], storageType: .userDefaults(store))
var acknowledgedHistoryVisibleRooms: Set<String>
@UserPreference(key: UserDefaultsKeys.hasSeenHistoryVisibleBannerRooms, defaultValue: [], storageType: .userDefaults(store))
var hasSeenHistoryVisibleBannerRooms: Set<String>

/// The initial set of account providers shown to the user in the authentication flow.
///
Expand Down Expand Up @@ -208,6 +209,8 @@ final class AppSettings {
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"
/// A URL where users can go to read more about room history sharing.
private(set) var historyVisibleDetailsURL: 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"]
/// The domain that account provisioning links will be hosted on - used for handling the links.
Expand Down
4 changes: 0 additions & 4 deletions ElementX/Sources/Generated/Strings+Untranslated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ import Foundation
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
internal enum UntranslatedL10n {
/// Messages you send will be shared with new members invited to this room. %1$s
internal static func cryptoHistoryVisible(_ p1: UnsafePointer<CChar>) -> String {
return UntranslatedL10n.tr("Untranslated", "crypto_history_visible", p1)
}
/// Clear all data currently stored on this device?
/// Sign in again to access your account data and messages.
internal static var softLogoutClearDataDialogContent: String { return UntranslatedL10n.tr("Untranslated", "soft_logout_clear_data_dialog_content") }
Expand Down
11 changes: 7 additions & 4 deletions ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
case .resolveVerificationViolation(let userID):
Task { await resolveIdentityVerificationViolation(userID) }
case .dismissHistoryVisibleAlert:
appSettings.acknowledgedHistoryVisibleRooms.insert(roomProxy.id)
appSettings.hasSeenHistoryVisibleBannerRooms.insert(roomProxy.id)
state.footerDetails = nil
}
case .acceptKnock(let eventID):
Expand Down Expand Up @@ -346,11 +346,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
state.canBan = powerLevels.canOwnUserBan()
}

// Whever the user opens a room with joined history visibility, we clear the dismiss flag to ensure that the banner is displayed again if the history is made visible in the future.
if roomInfo.historyVisibility == RoomHistoryVisibility.joined {
appSettings.acknowledgedHistoryVisibleRooms.remove(roomInfo.id)
appSettings.hasSeenHistoryVisibleBannerRooms.remove(roomInfo.id)
state.footerDetails = nil
} else if roomInfo.isEncrypted, !appSettings.acknowledgedHistoryVisibleRooms.contains(roomInfo.id) {
state.footerDetails = .historyVisible(learnMoreURL: "https://element.io")
}
// Whenever the user opens an encrypted room with non-join history visbility, we show them a warning banner if they have not already dismissed it.
else if roomInfo.isEncrypted, !appSettings.hasSeenHistoryVisibleBannerRooms.contains(roomInfo.id) {
state.footerDetails = .historyVisible(learnMoreURL: appSettings.historyVisibleDetailsURL)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,21 @@ struct RoomScreenFooterView: View {

private var borderColor: Color {
switch details {
case .pinViolation:
case .pinViolation, .historyVisible:
.compound.borderInfoSubtle
case .verificationViolation:
.compound.borderCriticalSubtle
case .historyVisible:
.compound.borderInfoSubtle
case .none:
Color.compound.bgCanvasDefault
}
}

private var gradient: Gradient {
switch details {
case .pinViolation:
case .pinViolation, .historyVisible:
.compound.info
case .verificationViolation:
Gradient(colors: [.compound.bgCriticalSubtle, .clear])
case .historyVisible:
Gradient(colors: [.compound.bgInfoSubtle, .clear])
case .none:
Gradient(colors: [.clear])
}
Expand All @@ -59,7 +55,7 @@ struct RoomScreenFooterView: View {
case .verificationViolation(member: let member, learnMoreURL: let learnMoreURL):
verificationViolation(member: member, learnMoreURL: learnMoreURL)
case .historyVisible(learnMoreURL: let learnMoreURL):
historyVisibleAlert(learnMoreUrl: learnMoreURL)
historyVisibleAlert(learnMoreURL: learnMoreURL)
}
}

Expand Down Expand Up @@ -157,18 +153,23 @@ struct RoomScreenFooterView: View {
return description
}

private func historyVisibleAlertDescriptionWithLearnMoreLink(learnMoreURL: URL) -> AttributedString {
let linkPlaceholder = "{link}"
var description = AttributedString(L10n.cryptoHistoryVisible(linkPlaceholder))
var linkString = AttributedString(L10n.actionLearnMore)
linkString.link = learnMoreURL
linkString.bold()
description.replace(linkPlaceholder, with: linkString)
return description
}

private func fallbackDisplayName(_ userID: String) -> String {
guard let localpart = userID.components(separatedBy: ":").first else { return userID }
return String(localpart.trimmingPrefix("@"))
}

private func historyVisibleAlert(learnMoreUrl: URL) -> some View {
let linkPlaceholder = "{link}"
var description = AttributedString(UntranslatedL10n.cryptoHistoryVisible(linkPlaceholder))
var linkString = AttributedString(L10n.actionLearnMore)
linkString.link = learnMoreUrl
linkString.bold()
description.replace(linkPlaceholder, with: linkString)
private func historyVisibleAlert(learnMoreURL: URL) -> some View {
let description = historyVisibleAlertDescriptionWithLearnMoreLink(learnMoreURL: learnMoreURL)

return VStack(spacing: 16) {
HStack(spacing: 16) {
Expand Down
1 change: 1 addition & 0 deletions ElementX/Sources/UITests/UITestsAppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class MockScreen: Identifiable {
deviceVerificationURL: appSettings.deviceVerificationURL,
chatBackupDetailsURL: appSettings.chatBackupDetailsURL,
identityPinningViolationDetailsURL: appSettings.identityPinningViolationDetailsURL,
historyVisibleDetailsURL: appSettings.historyVisibleDetailsURL,
elementWebHosts: appSettings.elementWebHosts,
accountProvisioningHost: appSettings.accountProvisioningHost,
bugReportApplicationID: appSettings.bugReportApplicationID,
Expand Down
16 changes: 8 additions & 8 deletions UnitTests/Sources/RoomScreenViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ class RoomScreenViewModelTests: XCTestCase {
let roomProxyMock = JoinedRoomProxyMock(configuration)

let roomInfoProxyMock = RoomInfoProxyMock(configuration)
roomInfoProxyMock.historyVisibility = RoomHistoryVisibility.joined
roomInfoProxyMock.historyVisibility = .joined

let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(roomInfoProxyMock)
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
Expand All @@ -489,13 +489,13 @@ class RoomScreenViewModelTests: XCTestCase {
}

func testHistoryVisibleBannerDoesNotAppearIfAcknowledged() async throws {
ServiceLocator.shared.settings.acknowledgedHistoryVisibleRooms.insert("$room:example.com")
ServiceLocator.shared.settings.hasSeenHistoryVisibleBannerRooms.insert("$room:example.com")

let configuration = JoinedRoomProxyMockConfiguration(id: "$room:example.com", isEncrypted: false)
let roomProxyMock = JoinedRoomProxyMock(configuration)

let roomInfoProxyMock = RoomInfoProxyMock(configuration)
roomInfoProxyMock.historyVisibility = RoomHistoryVisibility.shared
roomInfoProxyMock.historyVisibility = .shared

let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(roomInfoProxyMock)
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
Expand All @@ -522,7 +522,7 @@ class RoomScreenViewModelTests: XCTestCase {
let roomProxyMock = JoinedRoomProxyMock(configuration)

let roomInfoProxyMock = RoomInfoProxyMock(configuration)
roomInfoProxyMock.historyVisibility = RoomHistoryVisibility.shared
roomInfoProxyMock.historyVisibility = .shared

let infoSubject = CurrentValueSubject<RoomInfoProxyProtocol, Never>(roomInfoProxyMock)
roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher()
Expand All @@ -543,13 +543,13 @@ class RoomScreenViewModelTests: XCTestCase {
}
try await deferred.fulfill()

ServiceLocator.shared.settings.acknowledgedHistoryVisibleRooms.insert("$room:example.com")

viewModel.context.send(viewAction: RoomScreenViewAction.footerViewAction(RoomScreenFooterViewAction.dismissHistoryVisibleAlert))

deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.footerDetails == nil
}

ServiceLocator.shared.settings.hasSeenHistoryVisibleBannerRooms.insert("$room:example.com")
viewModel.context.send(viewAction: RoomScreenViewAction.footerViewAction(RoomScreenFooterViewAction.dismissHistoryVisibleAlert))

try await deferred.fulfill()
}
}
Loading