diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/RuleIds.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/RuleIds.kt index 4f35fb79c3..a3755f85b7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/RuleIds.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/RuleIds.kt @@ -47,8 +47,36 @@ object RuleIds { const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message" const val RULE_ID_ENCRYPTED = ".m.rule.encrypted" + const val RULE_ID_POLL_START_ONE_TO_ONE = ".m.rule.poll_start_one_to_one" + const val RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE = ".org.matrix.msc3930.rule.poll_start_one_to_one" + const val RULE_ID_POLL_END_ONE_TO_ONE = ".m.rule.poll_end_one_to_one" + const val RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE = ".org.matrix.msc3930.rule.poll_end_one_to_one" + + const val RULE_ID_POLL_START = ".m.rule.poll_start" + const val RULE_ID_POLL_START_UNSTABLE = ".org.matrix.msc3930.rule.poll_start" + const val RULE_ID_POLL_END = ".m.rule.poll_end" + const val RULE_ID_POLL_END_UNSTABLE = ".org.matrix.msc3930.rule.poll_end" + // Not documented const val RULE_ID_FALLBACK = ".m.rule.fallback" const val RULE_ID_REACTION = ".m.rule.reaction" + + fun getSyncedRules(ruleId: String): List { + return when (ruleId) { + RULE_ID_ONE_TO_ONE_ROOM -> listOf( + RULE_ID_POLL_START_ONE_TO_ONE, + RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, + RULE_ID_POLL_END_ONE_TO_ONE, + RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, + ) + RULE_ID_ALL_OTHER_MESSAGES_ROOMS -> listOf( + RULE_ID_POLL_START, + RULE_ID_POLL_START_UNSTABLE, + RULE_ID_POLL_END, + RULE_ID_POLL_END_UNSTABLE, + ) + else -> emptyList() + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt index 9498ed002c..9287a7828d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushrules/rest/RuleSet.kt @@ -47,21 +47,14 @@ data class RuleSet( * @param ruleId a RULE_ID_XX value * @return the matched bing rule or null it doesn't exist. */ - fun findDefaultRule(ruleId: String?): PushRuleAndKind? { - var result: PushRuleAndKind? = null - // sanity check - if (null != ruleId) { - if (RuleIds.RULE_ID_CONTAIN_USER_NAME == ruleId) { - result = findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) } - } else { - // assume that the ruleId is unique. - result = findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) } - if (null == result) { - result = findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) } - } - } + fun findDefaultRule(ruleId: String): PushRuleAndKind? { + return if (RuleIds.RULE_ID_CONTAIN_USER_NAME == ruleId) { + findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) } + } else { + // assume that the ruleId is unique. + findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) } + ?: findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) } } - return result } /** diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/PushRuleDefinitions.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/PushRuleDefinitions.kt index 264da7c4a3..fff6ffe933 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/PushRuleDefinitions.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/PushRuleDefinitions.kt @@ -38,7 +38,12 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.NOISY -> StandardActions.Highlight } - RuleIds.RULE_ID_ONE_TO_ONE_ROOM -> + RuleIds.RULE_ID_ONE_TO_ONE_ROOM, + RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, + RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, + RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, + RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, + -> when (index) { NotificationIndex.OFF -> StandardActions.DontNotify NotificationIndex.SILENT -> StandardActions.Notify @@ -50,7 +55,11 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.NOISY -> StandardActions.NotifyDefaultSound } - RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS -> + RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, + RuleIds.RULE_ID_POLL_START, + RuleIds.RULE_ID_POLL_START_UNSTABLE, + RuleIds.RULE_ID_POLL_END, + RuleIds.RULE_ID_POLL_END_UNSTABLE -> when (index) { NotificationIndex.OFF -> StandardActions.DontNotify NotificationIndex.SILENT -> StandardActions.Notify diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationFragment.kt index e53e84e5b4..0f991867fe 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationFragment.kt @@ -23,7 +23,6 @@ import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.core.preference.VectorCheckboxPreference import im.vector.app.features.settings.VectorSettingsBaseFragment -import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind abstract class VectorSettingsPushRuleNotificationFragment : VectorSettingsBaseFragment() { @@ -35,13 +34,20 @@ abstract class VectorSettingsPushRuleNotificationFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) observeViewEvents() + viewModel.onEach(VectorSettingsPushRuleNotificationViewState::isLoading) { isLoading -> + if (isLoading) { + displayLoadingView() + } else { + hideLoadingView() + } + } } private fun observeViewEvents() { viewModel.observeViewEvents { when (it) { is VectorSettingsPushRuleNotificationViewEvent.Failure -> refreshDisplay() - is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> updatePreference(it.ruleId, it.enabled) + is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> updatePreference(it.ruleId, it.checked) } } } @@ -50,14 +56,13 @@ abstract class VectorSettingsPushRuleNotificationFragment : for (preferenceKey in prefKeyToPushRuleId.keys) { val preference = findPreference(preferenceKey)!! preference.isIconSpaceReserved = false - val ruleAndKind: PushRuleAndKind? = session.pushRuleService().getPushRules().findDefaultRule(prefKeyToPushRuleId[preferenceKey]) + val ruleAndKind = prefKeyToPushRuleId[preferenceKey]?.let { viewModel.getPushRuleAndKind(it) } if (ruleAndKind == null) { // The rule is not defined, hide the preference preference.isVisible = false } else { preference.isVisible = true - val initialIndex = ruleAndKind.pushRule.notificationIndex - preference.isChecked = initialIndex != NotificationIndex.OFF + updatePreference(ruleAndKind.pushRule.ruleId, viewModel.isPushRuleChecked(ruleAndKind.pushRule.ruleId)) preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleAndKind, newValue as Boolean)) false @@ -81,7 +86,6 @@ abstract class VectorSettingsPushRuleNotificationFragment : private fun updatePreference(ruleId: String, checked: Boolean) { val preferenceKey = prefKeyToPushRuleId.entries.find { it.value == ruleId }?.key ?: return val preference = findPreference(preferenceKey) ?: return - preference.isChecked = checked } } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewEvent.kt index 6c316623e5..14d86d7ba7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewEvent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewEvent.kt @@ -19,6 +19,6 @@ package im.vector.app.features.settings.notifications import im.vector.app.core.platform.VectorViewEvents sealed interface VectorSettingsPushRuleNotificationViewEvent : VectorViewEvents { - data class PushRuleUpdated(val ruleId: String, val enabled: Boolean) : VectorSettingsPushRuleNotificationViewEvent + data class PushRuleUpdated(val ruleId: String, val checked: Boolean) : VectorSettingsPushRuleNotificationViewEvent data class Failure(val throwable: Throwable) : VectorSettingsPushRuleNotificationViewEvent } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewModel.kt index e284a32ec0..c681576ce0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationViewModel.kt @@ -27,6 +27,11 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.Failure import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.failure.Failure.ServerError +import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.pushrules.Action +import org.matrix.android.sdk.api.session.pushrules.RuleIds +import org.matrix.android.sdk.api.session.pushrules.RuleKind import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel @@ -52,6 +57,15 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor( } } + fun getPushRuleAndKind(ruleId: String): PushRuleAndKind? { + return activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.getPushRules()?.findDefaultRule(ruleId) + } + + fun isPushRuleChecked(ruleId: String): Boolean { + val rulesGroup = listOf(ruleId) + RuleIds.getSyncedRules(ruleId) + return rulesGroup.mapNotNull { getPushRuleAndKind(it) }.any { it.pushRule.notificationIndex != NotificationIndex.OFF } + } + private fun handleUpdatePushRule(pushRuleAndKind: PushRuleAndKind, checked: Boolean) { val ruleId = pushRuleAndKind.pushRule.ruleId val kind = pushRuleAndKind.kind @@ -62,23 +76,35 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor( setState { copy(isLoading = true) } viewModelScope.launch { - runCatching { - activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions( - kind = kind, - ruleId = ruleId, - enable = enabled, - actions = newActions - ) - }.fold( - onSuccess = { - setState { copy(isLoading = false) } - _viewEvents.post(PushRuleUpdated(ruleId, checked)) - }, - onFailure = { failure -> - setState { copy(isLoading = false) } - _viewEvents.post(Failure(failure)) - } + val rulesToUpdate = listOf(ruleId) + RuleIds.getSyncedRules(ruleId) + val results = rulesToUpdate.map { ruleId -> + runCatching { + updatePushRule(kind, ruleId, enabled, newActions) + } + } + setState { copy(isLoading = false) } + val failure = results.firstNotNullOfOrNull { it.exceptionOrNull() } + if (failure == null) { + _viewEvents.post(PushRuleUpdated(ruleId, checked)) + } else { + _viewEvents.post(Failure(failure)) + } + } + } + + private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List?) { + try { + activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions( + kind = kind, + ruleId = ruleId, + enable = enable, + actions = newActions ) + } catch (failure: ServerError) { + // Ignore the error if the rule id is not known from the server + if (failure.error.code != MatrixError.M_NOT_FOUND) { + throw failure + } } } }