Sync poll rules with messages rule

This commit is contained in:
Florian Renaud 2023-02-09 17:34:59 +01:00
parent 1c47983a99
commit 71455706cb
6 changed files with 99 additions and 39 deletions

View File

@ -47,8 +47,36 @@ object RuleIds {
const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message" const val RULE_ID_ALL_OTHER_MESSAGES_ROOMS = ".m.rule.message"
const val RULE_ID_ENCRYPTED = ".m.rule.encrypted" 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 // Not documented
const val RULE_ID_FALLBACK = ".m.rule.fallback" const val RULE_ID_FALLBACK = ".m.rule.fallback"
const val RULE_ID_REACTION = ".m.rule.reaction" const val RULE_ID_REACTION = ".m.rule.reaction"
fun getSyncedRules(ruleId: String): List<String> {
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()
}
}
} }

View File

@ -47,22 +47,15 @@ data class RuleSet(
* @param ruleId a RULE_ID_XX value * @param ruleId a RULE_ID_XX value
* @return the matched bing rule or null it doesn't exist. * @return the matched bing rule or null it doesn't exist.
*/ */
fun findDefaultRule(ruleId: String?): PushRuleAndKind? { fun findDefaultRule(ruleId: String): PushRuleAndKind? {
var result: PushRuleAndKind? = null return if (RuleIds.RULE_ID_CONTAIN_USER_NAME == ruleId) {
// sanity check findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) }
if (null != ruleId) {
if (RuleIds.RULE_ID_CONTAIN_USER_NAME == ruleId) {
result = findRule(content, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.CONTENT) }
} else { } else {
// assume that the ruleId is unique. // assume that the ruleId is unique.
result = findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) } findRule(override, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.OVERRIDE) }
if (null == result) { ?: findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) }
result = findRule(underride, ruleId)?.let { PushRuleAndKind(it, RuleSetKey.UNDERRIDE) }
} }
} }
}
return result
}
/** /**
* Find a rule from its rule Id. * Find a rule from its rule Id.

View File

@ -38,7 +38,12 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions
NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.SILENT -> StandardActions.Notify
NotificationIndex.NOISY -> StandardActions.Highlight 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) { when (index) {
NotificationIndex.OFF -> StandardActions.DontNotify NotificationIndex.OFF -> StandardActions.DontNotify
NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.SILENT -> StandardActions.Notify
@ -50,7 +55,11 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions
NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.SILENT -> StandardActions.Notify
NotificationIndex.NOISY -> StandardActions.NotifyDefaultSound 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) { when (index) {
NotificationIndex.OFF -> StandardActions.DontNotify NotificationIndex.OFF -> StandardActions.DontNotify
NotificationIndex.SILENT -> StandardActions.Notify NotificationIndex.SILENT -> StandardActions.Notify

View File

@ -23,7 +23,6 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.core.preference.VectorCheckboxPreference import im.vector.app.core.preference.VectorCheckboxPreference
import im.vector.app.features.settings.VectorSettingsBaseFragment import im.vector.app.features.settings.VectorSettingsBaseFragment
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
abstract class VectorSettingsPushRuleNotificationFragment : abstract class VectorSettingsPushRuleNotificationFragment :
VectorSettingsBaseFragment() { VectorSettingsBaseFragment() {
@ -35,13 +34,20 @@ abstract class VectorSettingsPushRuleNotificationFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
observeViewEvents() observeViewEvents()
viewModel.onEach(VectorSettingsPushRuleNotificationViewState::isLoading) { isLoading ->
if (isLoading) {
displayLoadingView()
} else {
hideLoadingView()
}
}
} }
private fun observeViewEvents() { private fun observeViewEvents() {
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
is VectorSettingsPushRuleNotificationViewEvent.Failure -> refreshDisplay() 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) { for (preferenceKey in prefKeyToPushRuleId.keys) {
val preference = findPreference<VectorCheckboxPreference>(preferenceKey)!! val preference = findPreference<VectorCheckboxPreference>(preferenceKey)!!
preference.isIconSpaceReserved = false preference.isIconSpaceReserved = false
val ruleAndKind: PushRuleAndKind? = session.pushRuleService().getPushRules().findDefaultRule(prefKeyToPushRuleId[preferenceKey]) val ruleAndKind = prefKeyToPushRuleId[preferenceKey]?.let { viewModel.getPushRuleAndKind(it) }
if (ruleAndKind == null) { if (ruleAndKind == null) {
// The rule is not defined, hide the preference // The rule is not defined, hide the preference
preference.isVisible = false preference.isVisible = false
} else { } else {
preference.isVisible = true preference.isVisible = true
val initialIndex = ruleAndKind.pushRule.notificationIndex updatePreference(ruleAndKind.pushRule.ruleId, viewModel.isPushRuleChecked(ruleAndKind.pushRule.ruleId))
preference.isChecked = initialIndex != NotificationIndex.OFF
preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleAndKind, newValue as Boolean)) viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleAndKind, newValue as Boolean))
false false
@ -81,7 +86,6 @@ abstract class VectorSettingsPushRuleNotificationFragment :
private fun updatePreference(ruleId: String, checked: Boolean) { private fun updatePreference(ruleId: String, checked: Boolean) {
val preferenceKey = prefKeyToPushRuleId.entries.find { it.value == ruleId }?.key ?: return val preferenceKey = prefKeyToPushRuleId.entries.find { it.value == ruleId }?.key ?: return
val preference = findPreference<VectorCheckboxPreference>(preferenceKey) ?: return val preference = findPreference<VectorCheckboxPreference>(preferenceKey) ?: return
preference.isChecked = checked preference.isChecked = checked
} }
} }

View File

@ -19,6 +19,6 @@ package im.vector.app.features.settings.notifications
import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewEvents
sealed interface VectorSettingsPushRuleNotificationViewEvent : 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 data class Failure(val throwable: Throwable) : VectorSettingsPushRuleNotificationViewEvent
} }

View File

@ -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.Failure
import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated import im.vector.app.features.settings.notifications.VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated
import kotlinx.coroutines.launch 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 import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel 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) { private fun handleUpdatePushRule(pushRuleAndKind: PushRuleAndKind, checked: Boolean) {
val ruleId = pushRuleAndKind.pushRule.ruleId val ruleId = pushRuleAndKind.pushRule.ruleId
val kind = pushRuleAndKind.kind val kind = pushRuleAndKind.kind
@ -62,23 +76,35 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
setState { copy(isLoading = true) } setState { copy(isLoading = true) }
viewModelScope.launch { viewModelScope.launch {
val rulesToUpdate = listOf(ruleId) + RuleIds.getSyncedRules(ruleId)
val results = rulesToUpdate.map { ruleId ->
runCatching { 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<Action>?) {
try {
activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions( activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions(
kind = kind, kind = kind,
ruleId = ruleId, ruleId = ruleId,
enable = enabled, enable = enable,
actions = newActions actions = newActions
) )
}.fold( } catch (failure: ServerError) {
onSuccess = { // Ignore the error if the rule id is not known from the server
setState { copy(isLoading = false) } if (failure.error.code != MatrixError.M_NOT_FOUND) {
_viewEvents.post(PushRuleUpdated(ruleId, checked)) throw failure
},
onFailure = { failure ->
setState { copy(isLoading = false) }
_viewEvents.post(Failure(failure))
} }
)
} }
} }
} }