From b1d82add913b4b2d7ac848a80fddb2080ab5eb7d Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 22 Apr 2026 17:03:10 +0200 Subject: [PATCH] stop showing live location disclaimer when accepted. --- .../Application/Settings/AppSettings.swift | 12 ++++++-- .../Mocks/Generated/GeneratedMocks.swift | 5 ++++ .../Mocks/LiveLocationManagerMock.swift | 3 ++ .../LocationSharingScreenViewModel.swift | 30 +++++++++++-------- .../Location/LiveLocationManager.swift | 9 ++++++ .../LiveLocationManagerProtocol.swift | 2 ++ 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/ElementX/Sources/Application/Settings/AppSettings.swift b/ElementX/Sources/Application/Settings/AppSettings.swift index 0185b84bc..d67c6762a 100644 --- a/ElementX/Sources/Application/Settings/AppSettings.swift +++ b/ElementX/Sources/Application/Settings/AppSettings.swift @@ -65,8 +65,11 @@ final class AppSettings { case elementCallBaseURLOverride case voiceMessagePlaybackSpeed + + // Live Location case liveLocationSharingTimeoutDatesByRoomID case liveLocationMinimumDistanceUpdate + case liveLocationDisclaimerDisplayed // Feature flags case publicSearchEnabled @@ -347,14 +350,19 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.hasRequestedLocationAlwaysLocationAuthorization, defaultValue: false, storageType: .userDefaults(store)) var hasRequestedLocationAlwaysLocationAuthorization + @UserPreference(key: UserDefaultsKeys.frequentlyUsedSystemEmojis, defaultValue: [FrequentlyUsedEmoji](), storageType: .userDefaults(store)) + var frequentlyUsedSystemEmojis + + // MARK: - Live Location + @UserPreference(key: UserDefaultsKeys.liveLocationSharingTimeoutDatesByRoomID, defaultValue: [String: Date](), storageType: .userDefaults(store)) var liveLocationSharingTimeoutDatesByRoomID @UserPreference(key: UserDefaultsKeys.liveLocationMinimumDistanceUpdate, defaultValue: 10, storageType: .userDefaults(store)) var liveLocationMinimumDistanceUpdate - @UserPreference(key: UserDefaultsKeys.frequentlyUsedSystemEmojis, defaultValue: [FrequentlyUsedEmoji](), storageType: .userDefaults(store)) - var frequentlyUsedSystemEmojis + @UserPreference(key: UserDefaultsKeys.liveLocationDisclaimerDisplayed, defaultValue: false, storageType: .userDefaults(store)) + var liveLocationDisclaimerDisplayed // MARK: - Home Screen diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index bf06c5258..086e82fd3 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -12041,6 +12041,11 @@ class LinkNewDeviceServiceMock: LinkNewDeviceServiceProtocol, @unchecked Sendabl } } class LiveLocationManagerMock: LiveLocationManagerProtocol, @unchecked Sendable { + var hasDisplayedLiveLocationDisclaimer: Bool { + get { return underlyingHasDisplayedLiveLocationDisclaimer } + set(value) { underlyingHasDisplayedLiveLocationDisclaimer = value } + } + var underlyingHasDisplayedLiveLocationDisclaimer: Bool! var authorizationStatus: CurrentValuePublisher { get { return underlyingAuthorizationStatus } set(value) { underlyingAuthorizationStatus = value } diff --git a/ElementX/Sources/Mocks/LiveLocationManagerMock.swift b/ElementX/Sources/Mocks/LiveLocationManagerMock.swift index 2126a8699..67b2ef71c 100644 --- a/ElementX/Sources/Mocks/LiveLocationManagerMock.swift +++ b/ElementX/Sources/Mocks/LiveLocationManagerMock.swift @@ -12,6 +12,7 @@ extension LiveLocationManagerMock { struct Configuration { var authorizationStatus: CLAuthorizationStatus = .notDetermined var requestAlwaysAuthorizationIfPossibleReturnValue = true + var hasDisplayedLiveLocationDisclaimer = false } convenience init(_ configuration: Configuration) { @@ -22,5 +23,7 @@ extension LiveLocationManagerMock { requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue startLiveLocationRoomIDDurationReturnValue = .success(()) + + underlyingHasDisplayedLiveLocationDisclaimer = configuration.hasDisplayedLiveLocationDisclaimer } } diff --git a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift index a4ef5fc70..5578be484 100644 --- a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift +++ b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift @@ -170,7 +170,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati let authorizationStatus = liveLocationManager.authorizationStatus.value switch authorizationStatus { case .authorizedAlways: - showLiveLocationDisclaimer() + showLiveLocationFlow() case .notDetermined: // This is to solve a race condition with map libre which always tries first // to request the when in use permission, we wait for it and then try again @@ -193,23 +193,29 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati .first() // this publisher only fires when there is an actual change, and if the user is done with permissions .sink { [weak self] newValue in guard newValue == .authorizedAlways else { return } - self?.showLiveLocationDisclaimer() + self?.showLiveLocationFlow() } default: showMissingAlwaysAuthorizedAlert() } } - private func showLiveLocationDisclaimer() { - state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer, - primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil), - secondaryButton: .init(title: L10n.actionAccept) { [weak self] in - // Delay so SwiftUI finishes dismissing the current alert - // before presenting the next one. - DispatchQueue.main.async { - self?.showLiveLocationDurationPicker() - } - }) + private func showLiveLocationFlow() { + if liveLocationManager.hasDisplayedLiveLocationDisclaimer { + showLiveLocationDurationPicker() + } else { + state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer, + primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil), + secondaryButton: .init(title: L10n.actionAccept) { [weak self] in + guard let self else { return } + liveLocationManager.hasDisplayedLiveLocationDisclaimer = true + // Delay so SwiftUI finishes dismissing the current alert + // before presenting the next one. + DispatchQueue.main.async { + self.showLiveLocationDurationPicker() + } + }) + } } private func showLiveLocationDurationPicker() { diff --git a/ElementX/Sources/Services/Location/LiveLocationManager.swift b/ElementX/Sources/Services/Location/LiveLocationManager.swift index f291d8785..572c51ba1 100644 --- a/ElementX/Sources/Services/Location/LiveLocationManager.swift +++ b/ElementX/Sources/Services/Location/LiveLocationManager.swift @@ -58,6 +58,15 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana // MARK: - LiveLocationManagerProtocol + var hasDisplayedLiveLocationDisclaimer: Bool { + get { + appSettings.liveLocationDisclaimerDisplayed + } + set { + appSettings.liveLocationDisclaimerDisplayed = newValue + } + } + @discardableResult func requestAlwaysAuthorizationIfPossible() -> Bool { guard !appSettings.hasRequestedLocationAlwaysLocationAuthorization else { return false } diff --git a/ElementX/Sources/Services/Location/LiveLocationManagerProtocol.swift b/ElementX/Sources/Services/Location/LiveLocationManagerProtocol.swift index 592063379..c65245625 100644 --- a/ElementX/Sources/Services/Location/LiveLocationManagerProtocol.swift +++ b/ElementX/Sources/Services/Location/LiveLocationManagerProtocol.swift @@ -16,6 +16,8 @@ enum LiveLocationManagerError: Error { // sourcery: AutoMockable protocol LiveLocationManagerProtocol: AnyObject { + /// True if the live location disclaimer has been displayed already, will only be displayed once. + var hasDisplayedLiveLocationDisclaimer: Bool { get set } /// Publishes the current location authorization status. var authorizationStatus: CurrentValuePublisher { get }