diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 5695d6723..a8489a4f7 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -294,6 +294,8 @@ final class BuildSettings: NSObject { static let settingsSecurityScreenShowCryptographyInfo:Bool = true static let settingsSecurityScreenShowCryptographyExport:Bool = true static let settingsSecurityScreenShowAdvancedUnverifiedDevices:Bool = true + /// A setting to enable the presence configuration settings section. + static let settingsScreenPresenceAllowConfiguration: Bool = false // MARK: - Timeline settings static let roomInputToolbarCompressionMode: MediaCompressionMode = .prompt diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 8f6405336..501fba8c1 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -750,6 +750,10 @@ Tap the + to start adding people."; "settings_enable_room_message_bubbles" = "Message bubbles"; +"settings_presence" = "Presence"; +"settings_presence_offline_mode" = "Offline Mode"; +"settings_presence_offline_mode_description" = "If enabled, you will always appear offline to other users, even when using the application."; + // Security settings "security_settings_title" = "Security"; "security_settings_crypto_sessions" = "MY SESSIONS"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 34b6299d6..3489286b9 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -6895,6 +6895,18 @@ public class VectorL10n: NSObject { public static var settingsPinRoomsWithUnread: String { return VectorL10n.tr("Vector", "settings_pin_rooms_with_unread") } + /// Presence + public static var settingsPresence: String { + return VectorL10n.tr("Vector", "settings_presence") + } + /// Offline Mode + public static var settingsPresenceOfflineMode: String { + return VectorL10n.tr("Vector", "settings_presence_offline_mode") + } + /// If enabled, you will always appear offline to other users, even when using the application. + public static var settingsPresenceOfflineModeDescription: String { + return VectorL10n.tr("Vector", "settings_presence_offline_mode_description") + } /// Privacy Policy public static var settingsPrivacyPolicy: String { return VectorL10n.tr("Vector", "settings_privacy_policy") diff --git a/Riot/Modules/Common/PresenceIndicator/PresenceIndicatorView.swift b/Riot/Modules/Common/PresenceIndicator/PresenceIndicatorView.swift index 2380368eb..aff0a3a89 100644 --- a/Riot/Modules/Common/PresenceIndicator/PresenceIndicatorView.swift +++ b/Riot/Modules/Common/PresenceIndicator/PresenceIndicatorView.swift @@ -63,10 +63,10 @@ final class PresenceIndicatorView: UIView { /// - presence: `MXPresence` to display func setPresence(_ presence: MXPresence) { switch presence { - case MXPresenceOnline: + case .online: self.backgroundColor = ThemeService.shared().theme.tintColor self.borderLayer.borderColor = self.borderColor.cgColor - case MXPresenceOffline, MXPresenceUnavailable: + case .offline, .unavailable: self.backgroundColor = ThemeService.shared().theme.tabBarUnselectedItemTintColor self.borderLayer.borderColor = self.borderColor.cgColor default: diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m index 728a90fa1..4de8b53a6 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m @@ -715,6 +715,7 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; // Instantiate new session mxSession = [[MXSession alloc] initWithMatrixRestClient:mxRestClient]; + mxSession.preferredSyncPresence = self.preferredSyncPresence; // Check whether an antivirus url is defined. if (_antivirusServerURL) @@ -1007,7 +1008,7 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; // Update user presence MXWeakify(self); - [self setUserPresence:MXPresenceUnavailable andStatusMessage:nil completion:^{ + [self setUserPresence:MXPresenceOffline andStatusMessage:nil completion:^{ MXStrongifyAndReturnIfNil(self); [self cancelPauseBackgroundTask]; }]; @@ -1045,8 +1046,10 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; case MXSessionStatePauseRequested: { // Resume SDK and update user presence + MXWeakify(self); [mxSession resume:^{ - [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; + MXStrongifyAndReturnIfNil(self); + [self setUserPresence:self.preferredSyncPresence andStatusMessage:nil completion:nil]; [self refreshAPNSPusher]; [self refreshPushKitPusher]; @@ -1513,7 +1516,7 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; MXLogDebug(@"[MXKAccount] %@: The session is ready. Matrix SDK session has been started in %0.fms.", self.mxCredentials.userId, [[NSDate date] timeIntervalSinceDate:self->openSessionStartDate] * 1000); - [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; + [self setUserPresence:self.preferredSyncPresence andStatusMessage:nil completion:nil]; } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); @@ -2159,4 +2162,20 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; } } +#pragma mark - Presence + +- (void)setPreferredSyncPresence:(MXPresence)preferredSyncPresence +{ + [super setPreferredSyncPresence:preferredSyncPresence]; + + if (self.mxSession) + { + self.mxSession.preferredSyncPresence = preferredSyncPresence; + [self setUserPresence:preferredSyncPresence andStatusMessage:nil completion:nil]; + } + + // Archive updated field + [[MXKAccountManager sharedManager] saveAccounts]; +} + @end diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h index 3098f66d1..ed49eb55c 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.h @@ -32,6 +32,7 @@ @protected BOOL _isSoftLogout; @protected BOOL _hasPusherForPushNotifications; @protected BOOL _hasPusherForPushKitNotifications; +@protected MXPresence _preferredSyncPresence; } /** @@ -89,6 +90,12 @@ */ @property (nonatomic, readonly) BOOL hasPusherForPushKitNotifications; +/** + The account's preferred Presence status to share while the application is in foreground. + Defaults to MXPresenceOnline. + */ +@property (nonatomic) MXPresence preferredSyncPresence; + /** Enable In-App notifications based on Remote notifications rules. diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m index 4d59e2a4b..e878ffb25 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountData.m @@ -80,6 +80,16 @@ _warnedAboutEncryption = [coder decodeBoolForKey:@"warnedAboutEncryption"]; + if ([coder decodeObjectOfClass:NSString.class forKey:@"preferredSyncPresence"]) + { + MXPresenceString presenceString = [coder decodeObjectOfClass:NSString.class forKey:@"preferredSyncPresence"]; + _preferredSyncPresence = [MXTools presence:presenceString]; + } + else + { + _preferredSyncPresence = MXPresenceOnline; + } + _others = [coder decodeObjectForKey:@"others"]; } @@ -143,6 +153,9 @@ [coder encodeBool:_warnedAboutEncryption forKey:@"warnedAboutEncryption"]; + MXPresenceString presenceString = [MXTools presenceString:_preferredSyncPresence]; + [coder encodeObject:presenceString forKey:@"preferredSyncPresence"]; + [coder encodeObject:_others forKey:@"others"]; } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModel.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModel.swift index cca5c9064..14f50131b 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModel.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModel.swift @@ -34,7 +34,7 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType { private var viewData: RoomInfoListViewData { let encryptionImage = EncryptionTrustLevelBadgeImageHelper.roomBadgeImage(for: room.summary.roomEncryptionTrustLevel()) - let directUserPresence = session.user(withUserId: room.directUserId)?.presence ?? MXPresenceUnknown + let directUserPresence = session.user(withUserId: room.directUserId)?.presence ?? .unknown let basicInfoViewData = RoomInfoBasicViewData(avatarUrl: room.summary.avatar, mediaManager: session.mediaManager, diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.swift index 4332adfda..be87ad01e 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.swift @@ -111,7 +111,7 @@ class RoomInfoBasicView: UIView { badgeImageView.isHidden = false // Update badge position if it doesn't match expectation. // If presence is displayed, badge should be in the name stack. - let isPresenceDisplayed = presence != MXPresenceUnknown + let isPresenceDisplayed = presence != .unknown let isBadgeInRoomNameStackView = roomNameStackView.arrangedSubviews.contains(badgeImageView) switch (isPresenceDisplayed, isBadgeInRoomNameStackView) { case (true, false): diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.xib b/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.xib index fec6b60b9..68003b709 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.xib +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/Views/RoomInfoBasicView.xib @@ -87,6 +87,9 @@ + + +