stop showing live location disclaimer when accepted.

This commit is contained in:
Mauro Romito
2026-04-22 17:03:10 +02:00
committed by Mauro
parent f23b77a481
commit 6dc6eed9e3
6 changed files with 47 additions and 14 deletions
@@ -65,8 +65,11 @@ final class AppSettings {
case elementCallBaseURLOverride case elementCallBaseURLOverride
case voiceMessagePlaybackSpeed case voiceMessagePlaybackSpeed
// Live Location
case liveLocationSharingTimeoutDatesByRoomID case liveLocationSharingTimeoutDatesByRoomID
case liveLocationMinimumDistanceUpdate case liveLocationMinimumDistanceUpdate
case liveLocationDisclaimerDisplayed
// Feature flags // Feature flags
case publicSearchEnabled case publicSearchEnabled
@@ -347,14 +350,19 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.hasRequestedLocationAlwaysLocationAuthorization, defaultValue: false, storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.hasRequestedLocationAlwaysLocationAuthorization, defaultValue: false, storageType: .userDefaults(store))
var hasRequestedLocationAlwaysLocationAuthorization 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)) @UserPreference(key: UserDefaultsKeys.liveLocationSharingTimeoutDatesByRoomID, defaultValue: [String: Date](), storageType: .userDefaults(store))
var liveLocationSharingTimeoutDatesByRoomID var liveLocationSharingTimeoutDatesByRoomID
@UserPreference(key: UserDefaultsKeys.liveLocationMinimumDistanceUpdate, defaultValue: 10, storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.liveLocationMinimumDistanceUpdate, defaultValue: 10, storageType: .userDefaults(store))
var liveLocationMinimumDistanceUpdate var liveLocationMinimumDistanceUpdate
@UserPreference(key: UserDefaultsKeys.frequentlyUsedSystemEmojis, defaultValue: [FrequentlyUsedEmoji](), storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.liveLocationDisclaimerDisplayed, defaultValue: false, storageType: .userDefaults(store))
var frequentlyUsedSystemEmojis var liveLocationDisclaimerDisplayed
// MARK: - Home Screen // MARK: - Home Screen
@@ -12041,6 +12041,11 @@ class LinkNewDeviceServiceMock: LinkNewDeviceServiceProtocol, @unchecked Sendabl
} }
} }
class LiveLocationManagerMock: LiveLocationManagerProtocol, @unchecked Sendable { class LiveLocationManagerMock: LiveLocationManagerProtocol, @unchecked Sendable {
var hasDisplayedLiveLocationDisclaimer: Bool {
get { return underlyingHasDisplayedLiveLocationDisclaimer }
set(value) { underlyingHasDisplayedLiveLocationDisclaimer = value }
}
var underlyingHasDisplayedLiveLocationDisclaimer: Bool!
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> { var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> {
get { return underlyingAuthorizationStatus } get { return underlyingAuthorizationStatus }
set(value) { underlyingAuthorizationStatus = value } set(value) { underlyingAuthorizationStatus = value }
@@ -12,6 +12,7 @@ extension LiveLocationManagerMock {
struct Configuration { struct Configuration {
var authorizationStatus: CLAuthorizationStatus = .notDetermined var authorizationStatus: CLAuthorizationStatus = .notDetermined
var requestAlwaysAuthorizationIfPossibleReturnValue = true var requestAlwaysAuthorizationIfPossibleReturnValue = true
var hasDisplayedLiveLocationDisclaimer = false
} }
convenience init(_ configuration: Configuration) { convenience init(_ configuration: Configuration) {
@@ -22,5 +23,7 @@ extension LiveLocationManagerMock {
requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue requestAlwaysAuthorizationIfPossibleReturnValue = configuration.requestAlwaysAuthorizationIfPossibleReturnValue
startLiveLocationRoomIDDurationReturnValue = .success(()) startLiveLocationRoomIDDurationReturnValue = .success(())
underlyingHasDisplayedLiveLocationDisclaimer = configuration.hasDisplayedLiveLocationDisclaimer
} }
} }
@@ -170,7 +170,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
let authorizationStatus = liveLocationManager.authorizationStatus.value let authorizationStatus = liveLocationManager.authorizationStatus.value
switch authorizationStatus { switch authorizationStatus {
case .authorizedAlways: case .authorizedAlways:
showLiveLocationDisclaimer() showLiveLocationFlow()
case .notDetermined: case .notDetermined:
// This is to solve a race condition with map libre which always tries first // 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 // 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 .first() // this publisher only fires when there is an actual change, and if the user is done with permissions
.sink { [weak self] newValue in .sink { [weak self] newValue in
guard newValue == .authorizedAlways else { return } guard newValue == .authorizedAlways else { return }
self?.showLiveLocationDisclaimer() self?.showLiveLocationFlow()
} }
default: default:
showMissingAlwaysAuthorizedAlert() showMissingAlwaysAuthorizedAlert()
} }
} }
private func showLiveLocationDisclaimer() { private func showLiveLocationFlow() {
state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer, if liveLocationManager.hasDisplayedLiveLocationDisclaimer {
primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil), showLiveLocationDurationPicker()
secondaryButton: .init(title: L10n.actionAccept) { [weak self] in } else {
// Delay so SwiftUI finishes dismissing the current alert state.bindings.alertInfo = .init(alertID: .liveLocationDisclaimer,
// before presenting the next one. primaryButton: .init(title: L10n.actionDecline, role: .cancel, action: nil),
DispatchQueue.main.async { secondaryButton: .init(title: L10n.actionAccept) { [weak self] in
self?.showLiveLocationDurationPicker() 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() { private func showLiveLocationDurationPicker() {
@@ -58,6 +58,15 @@ class LiveLocationManager: NSObject, LiveLocationManagerProtocol, CLLocationMana
// MARK: - LiveLocationManagerProtocol // MARK: - LiveLocationManagerProtocol
var hasDisplayedLiveLocationDisclaimer: Bool {
get {
appSettings.liveLocationDisclaimerDisplayed
}
set {
appSettings.liveLocationDisclaimerDisplayed = newValue
}
}
@discardableResult @discardableResult
func requestAlwaysAuthorizationIfPossible() -> Bool { func requestAlwaysAuthorizationIfPossible() -> Bool {
guard !appSettings.hasRequestedLocationAlwaysLocationAuthorization else { return false } guard !appSettings.hasRequestedLocationAlwaysLocationAuthorization else { return false }
@@ -16,6 +16,8 @@ enum LiveLocationManagerError: Error {
// sourcery: AutoMockable // sourcery: AutoMockable
protocol LiveLocationManagerProtocol: AnyObject { 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. /// Publishes the current location authorization status.
var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> { get } var authorizationStatus: CurrentValuePublisher<CLAuthorizationStatus, Never> { get }