Merge pull request #8141 from vector-im/feature/fre/poll_sync_error_handling
[Poll] Error handling for push rules synchronization
This commit is contained in:
commit
ea635976a5
|
@ -0,0 +1 @@
|
||||||
|
[Poll] Error handling for push rules synchronization
|
|
@ -862,6 +862,8 @@
|
||||||
<string name="settings_notification_keyword_contains_dot">Keywords cannot start with \'.\'</string>
|
<string name="settings_notification_keyword_contains_dot">Keywords cannot start with \'.\'</string>
|
||||||
<string name="settings_notification_keyword_contains_invalid_character">Keywords cannot contain \'%s\'</string>
|
<string name="settings_notification_keyword_contains_invalid_character">Keywords cannot contain \'%s\'</string>
|
||||||
|
|
||||||
|
<string name="settings_notification_error_on_update">An error occurred when updating your notification preferences. Please try again.</string>
|
||||||
|
|
||||||
<string name="settings_notification_troubleshoot">Troubleshoot Notifications</string>
|
<string name="settings_notification_troubleshoot">Troubleshoot Notifications</string>
|
||||||
<string name="settings_troubleshoot_diagnostic">Troubleshooting diagnostics</string>
|
<string name="settings_troubleshoot_diagnostic">Troubleshooting diagnostics</string>
|
||||||
<string name="settings_troubleshoot_diagnostic_run_button_title">Run Tests</string>
|
<string name="settings_troubleshoot_diagnostic_run_button_title">Run Tests</string>
|
||||||
|
|
|
@ -488,7 +488,7 @@ fun Event.getPollContent(): MessagePollContent? {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.supportsNotification() =
|
fun Event.supportsNotification() =
|
||||||
this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.STATE_ROOM_BEACON_INFO.values
|
this.getClearType() in EventType.MESSAGE + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values
|
||||||
|
|
||||||
fun Event.isContentReportable() =
|
fun Event.isContentReportable() =
|
||||||
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values
|
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values
|
||||||
|
|
|
@ -32,6 +32,8 @@ class VectorCheckboxPreference : CheckBoxPreference {
|
||||||
|
|
||||||
constructor(context: Context) : super(context)
|
constructor(context: Context) : super(context)
|
||||||
|
|
||||||
|
var summaryTextColor: Int? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Set to false to remove the space when there is no icon
|
// Set to false to remove the space when there is no icon
|
||||||
isIconSpaceReserved = true
|
isIconSpaceReserved = true
|
||||||
|
@ -42,4 +44,9 @@ class VectorCheckboxPreference : CheckBoxPreference {
|
||||||
(holder.findViewById(android.R.id.title) as? TextView)?.isSingleLine = false
|
(holder.findViewById(android.R.id.title) as? TextView)?.isSingleLine = false
|
||||||
super.onBindViewHolder(holder)
|
super.onBindViewHolder(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun syncSummaryView(holder: PreferenceViewHolder) {
|
||||||
|
super.syncSummaryView(holder)
|
||||||
|
summaryTextColor?.let { (holder.findViewById(android.R.id.summary) as? TextView)?.setTextColor(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,7 @@ fun getStandardAction(ruleId: String, index: NotificationIndex): StandardActions
|
||||||
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE,
|
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE,
|
||||||
RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE,
|
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,
|
||||||
RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE,
|
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
|
||||||
|
|
|
@ -21,64 +21,79 @@ import android.view.View
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
import im.vector.app.R
|
||||||
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 im.vector.app.features.themes.ThemeUtils
|
||||||
|
|
||||||
abstract class VectorSettingsPushRuleNotificationFragment :
|
abstract class VectorSettingsPushRuleNotificationFragment :
|
||||||
VectorSettingsBaseFragment() {
|
VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
private val viewModel: VectorSettingsPushRuleNotificationViewModel by fragmentViewModel()
|
protected val viewModel: VectorSettingsPushRuleNotificationViewModel by fragmentViewModel()
|
||||||
|
|
||||||
abstract val prefKeyToPushRuleId: Map<String, String>
|
abstract val prefKeyToPushRuleId: Map<String, String>
|
||||||
|
|
||||||
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 ->
|
viewModel.onEach(VectorSettingsPushRuleNotificationViewState::allRules) { refreshPreferences() }
|
||||||
if (isLoading) {
|
viewModel.onEach(VectorSettingsPushRuleNotificationViewState::isLoading) { updateLoadingView(it) }
|
||||||
displayLoadingView()
|
viewModel.onEach(VectorSettingsPushRuleNotificationViewState::rulesOnError) { refreshErrors(it) }
|
||||||
} else {
|
|
||||||
hideLoadingView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeViewEvents() {
|
private fun observeViewEvents() {
|
||||||
viewModel.observeViewEvents {
|
viewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is VectorSettingsPushRuleNotificationViewEvent.Failure -> refreshDisplay()
|
is VectorSettingsPushRuleNotificationViewEvent.Failure -> onFailure(it.ruleId)
|
||||||
is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> updatePreference(it.ruleId, it.checked)
|
is VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated -> {
|
||||||
|
updatePreference(it.ruleId, it.checked)
|
||||||
|
if (it.failure != null) {
|
||||||
|
onFailure(it.ruleId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindPref() {
|
override fun bindPref() {
|
||||||
for (preferenceKey in prefKeyToPushRuleId.keys) {
|
prefKeyToPushRuleId.forEach { (preferenceKey, ruleId) ->
|
||||||
val preference = findPreference<VectorCheckboxPreference>(preferenceKey)!!
|
findPreference<VectorCheckboxPreference>(preferenceKey)?.apply {
|
||||||
preference.isIconSpaceReserved = false
|
isIconSpaceReserved = false
|
||||||
val ruleAndKind = prefKeyToPushRuleId[preferenceKey]?.let { viewModel.getPushRuleAndKind(it) }
|
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
if (ruleAndKind == null) {
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleId, newValue as Boolean))
|
||||||
// The rule is not defined, hide the preference
|
|
||||||
preference.isVisible = false
|
|
||||||
} else {
|
|
||||||
preference.isVisible = true
|
|
||||||
updatePreference(ruleAndKind.pushRule.ruleId, viewModel.isPushRuleChecked(ruleAndKind.pushRule.ruleId))
|
|
||||||
preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(ruleAndKind, newValue as Boolean))
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
private fun updateLoadingView(isLoading: Boolean) {
|
||||||
if (state.isLoading) {
|
if (isLoading) {
|
||||||
displayLoadingView()
|
displayLoadingView()
|
||||||
} else {
|
} else {
|
||||||
hideLoadingView()
|
hideLoadingView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshPreferences() {
|
||||||
|
prefKeyToPushRuleId.values.forEach { ruleId -> updatePreference(ruleId, viewModel.isPushRuleChecked(ruleId)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshErrors(rulesWithError: Set<String>) {
|
||||||
|
if (withState(viewModel, VectorSettingsPushRuleNotificationViewState::isLoading)) return
|
||||||
|
prefKeyToPushRuleId.forEach { (preferenceKey, ruleId) ->
|
||||||
|
findPreference<VectorCheckboxPreference>(preferenceKey)?.apply {
|
||||||
|
if (ruleId in rulesWithError) {
|
||||||
|
summaryTextColor = ThemeUtils.getColor(context, R.attr.colorError)
|
||||||
|
setSummary(R.string.settings_notification_error_on_update)
|
||||||
|
} else {
|
||||||
|
summaryTextColor = null
|
||||||
|
summary = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected fun refreshDisplay() {
|
protected fun refreshDisplay() {
|
||||||
listView?.adapter?.notifyDataSetChanged()
|
listView?.adapter?.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
@ -86,6 +101,12 @@ 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
|
||||||
|
val ruleIds = withState(viewModel) { state -> state.allRules.map { it.ruleId } }
|
||||||
|
preference.isVisible = ruleId in ruleIds
|
||||||
preference.isChecked = checked
|
preference.isChecked = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun onFailure(ruleId: String) {
|
||||||
|
refreshDisplay()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
package im.vector.app.features.settings.notifications
|
package im.vector.app.features.settings.notifications
|
||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
|
||||||
|
|
||||||
sealed interface VectorSettingsPushRuleNotificationViewAction : VectorViewModelAction {
|
sealed interface VectorSettingsPushRuleNotificationViewAction : VectorViewModelAction {
|
||||||
data class UpdatePushRule(val pushRuleAndKind: PushRuleAndKind, val checked: Boolean) : VectorSettingsPushRuleNotificationViewAction
|
data class UpdatePushRule(val ruleId: String, val checked: Boolean) : VectorSettingsPushRuleNotificationViewAction
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ sealed interface VectorSettingsPushRuleNotificationViewEvent : VectorViewEvents
|
||||||
/**
|
/**
|
||||||
* A failure has occurred.
|
* A failure has occurred.
|
||||||
*
|
*
|
||||||
|
* @property ruleId the global rule id related to the failure.
|
||||||
* @property throwable the related exception, if any.
|
* @property throwable the related exception, if any.
|
||||||
*/
|
*/
|
||||||
data class Failure(val throwable: Throwable?) : VectorSettingsPushRuleNotificationViewEvent
|
data class Failure(val ruleId: String, val throwable: Throwable?) : VectorSettingsPushRuleNotificationViewEvent
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,26 +20,31 @@ import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
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 im.vector.app.features.settings.notifications.usecase.GetPushRulesOnInvalidStateUseCase
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.failure.Failure.ServerError
|
import org.matrix.android.sdk.api.failure.Failure.ServerError
|
||||||
import org.matrix.android.sdk.api.failure.MatrixError
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.pushrules.Action
|
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.RuleIds
|
||||||
import org.matrix.android.sdk.api.session.pushrules.RuleKind
|
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
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
|
import org.matrix.android.sdk.flow.unwrap
|
||||||
|
|
||||||
private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel
|
private typealias ViewModel = VectorSettingsPushRuleNotificationViewModel
|
||||||
private typealias ViewState = VectorSettingsPushRuleNotificationViewState
|
private typealias ViewState = VectorSettingsPushRuleNotificationViewState
|
||||||
|
|
||||||
class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: ViewState,
|
@Assisted initialState: ViewState,
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
private val session: Session,
|
||||||
|
private val getPushRulesOnInvalidStateUseCase: GetPushRulesOnInvalidStateUseCase,
|
||||||
) : VectorViewModel<VectorSettingsPushRuleNotificationViewState,
|
) : VectorViewModel<VectorSettingsPushRuleNotificationViewState,
|
||||||
VectorSettingsPushRuleNotificationViewAction,
|
VectorSettingsPushRuleNotificationViewAction,
|
||||||
VectorSettingsPushRuleNotificationViewEvent>(initialState) {
|
VectorSettingsPushRuleNotificationViewEvent>(initialState) {
|
||||||
|
@ -51,14 +56,28 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<ViewModel, ViewState> by hiltMavericksViewModelFactory()
|
companion object : MavericksViewModelFactory<ViewModel, ViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
init {
|
||||||
|
session.flow()
|
||||||
|
.liveUserAccountData(UserAccountDataTypes.TYPE_PUSH_RULES)
|
||||||
|
.unwrap()
|
||||||
|
.setOnEach {
|
||||||
|
val allRules = session.pushRuleService().getPushRules().getAllRules()
|
||||||
|
val rulesOnError = getPushRulesOnInvalidStateUseCase.execute(session).map { it.ruleId }.toSet()
|
||||||
|
copy(
|
||||||
|
allRules = allRules,
|
||||||
|
rulesOnError = rulesOnError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: VectorSettingsPushRuleNotificationViewAction) {
|
override fun handle(action: VectorSettingsPushRuleNotificationViewAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is VectorSettingsPushRuleNotificationViewAction.UpdatePushRule -> handleUpdatePushRule(action.pushRuleAndKind, action.checked)
|
is VectorSettingsPushRuleNotificationViewAction.UpdatePushRule -> handleUpdatePushRule(action.ruleId, action.checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPushRuleAndKind(ruleId: String): PushRuleAndKind? {
|
fun getPushRuleAndKind(ruleId: String): PushRuleAndKind? {
|
||||||
return activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.getPushRules()?.findDefaultRule(ruleId)
|
return session.pushRuleService().getPushRules().findDefaultRule(ruleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPushRuleChecked(ruleId: String): Boolean {
|
fun isPushRuleChecked(ruleId: String): Boolean {
|
||||||
|
@ -66,13 +85,13 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
||||||
return rulesGroup.mapNotNull { getPushRuleAndKind(it) }.any { it.pushRule.notificationIndex != NotificationIndex.OFF }
|
return rulesGroup.mapNotNull { getPushRuleAndKind(it) }.any { it.pushRule.notificationIndex != NotificationIndex.OFF }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUpdatePushRule(pushRuleAndKind: PushRuleAndKind, checked: Boolean) {
|
private fun handleUpdatePushRule(ruleId: String, checked: Boolean) {
|
||||||
val ruleId = pushRuleAndKind.pushRule.ruleId
|
val kind = getPushRuleAndKind(ruleId)?.kind ?: return
|
||||||
val kind = pushRuleAndKind.kind
|
|
||||||
val newIndex = if (checked) NotificationIndex.NOISY else NotificationIndex.OFF
|
val newIndex = if (checked) NotificationIndex.NOISY else NotificationIndex.OFF
|
||||||
val standardAction = getStandardAction(ruleId, newIndex) ?: return
|
val standardAction = getStandardAction(ruleId, newIndex) ?: return
|
||||||
val enabled = standardAction != StandardActions.Disabled
|
val enabled = standardAction != StandardActions.Disabled
|
||||||
val newActions = standardAction.actions
|
val newActions = standardAction.actions
|
||||||
|
|
||||||
setState { copy(isLoading = true) }
|
setState { copy(isLoading = true) }
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
@ -82,28 +101,37 @@ class VectorSettingsPushRuleNotificationViewModel @AssistedInject constructor(
|
||||||
updatePushRule(kind, ruleId, enabled, newActions)
|
updatePushRule(kind, ruleId, enabled, newActions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setState { copy(isLoading = false) }
|
|
||||||
val failure = results.firstNotNullOfOrNull { result ->
|
val failures = results.mapNotNull { result ->
|
||||||
// If the failure is a rule not found error, do not consider it
|
// If the failure is a rule not found error, do not consider it
|
||||||
result.exceptionOrNull()?.takeUnless { it is ServerError && it.error.code == MatrixError.M_NOT_FOUND }
|
result.exceptionOrNull()?.takeUnless { it is ServerError && it.error.code == MatrixError.M_NOT_FOUND }
|
||||||
}
|
}
|
||||||
val newChecked = if (checked) {
|
val hasSuccess = results.any { it.isSuccess }
|
||||||
// If any rule is checked, the global rule is checked
|
val hasFailures = failures.isNotEmpty()
|
||||||
results.any { it.isSuccess }
|
|
||||||
|
// Any rule has been checked or some rules have not been unchecked
|
||||||
|
val newChecked = (checked && hasSuccess) || (!checked && hasFailures)
|
||||||
|
if (hasSuccess) {
|
||||||
|
_viewEvents.post(PushRuleUpdated(ruleId, newChecked, failures.firstOrNull()))
|
||||||
} else {
|
} else {
|
||||||
// If any rule has not been unchecked, the global rule remains checked
|
_viewEvents.post(Failure(ruleId, failures.firstOrNull()))
|
||||||
failure != null
|
|
||||||
}
|
}
|
||||||
if (results.any { it.isSuccess }) {
|
|
||||||
_viewEvents.post(PushRuleUpdated(ruleId, newChecked, failure))
|
setState {
|
||||||
} else {
|
copy(
|
||||||
_viewEvents.post(Failure(failure))
|
isLoading = false,
|
||||||
|
rulesOnError = when {
|
||||||
|
hasSuccess && hasFailures -> rulesOnError.plus(ruleId) // some failed
|
||||||
|
hasSuccess -> rulesOnError.minus(ruleId) // all succeed
|
||||||
|
else -> rulesOnError // all failed
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List<Action>?) {
|
private suspend fun updatePushRule(kind: RuleKind, ruleId: String, enable: Boolean, newActions: List<Action>?) {
|
||||||
activeSessionHolder.getSafeActiveSession()?.pushRuleService()?.updatePushRuleActions(
|
session.pushRuleService().updatePushRuleActions(
|
||||||
kind = kind,
|
kind = kind,
|
||||||
ruleId = ruleId,
|
ruleId = ruleId,
|
||||||
enable = enable,
|
enable = enable,
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
package im.vector.app.features.settings.notifications
|
package im.vector.app.features.settings.notifications
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
|
|
||||||
data class VectorSettingsPushRuleNotificationViewState(
|
data class VectorSettingsPushRuleNotificationViewState(
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
|
val allRules: List<PushRule> = emptyList(),
|
||||||
|
val rulesOnError: Set<String> = emptySet(),
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.settings.notifications.usecase
|
||||||
|
|
||||||
|
import im.vector.app.features.settings.notifications.getParentRule
|
||||||
|
import im.vector.app.features.settings.notifications.getSyncedRules
|
||||||
|
import org.matrix.android.sdk.api.extensions.orTrue
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.RuleIds
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.getActions
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetPushRulesOnInvalidStateUseCase @Inject constructor() {
|
||||||
|
|
||||||
|
fun execute(session: Session): List<PushRule> {
|
||||||
|
val allRules = session.pushRuleService().getPushRules().getAllRules()
|
||||||
|
return allRules.filter { it.isOnInvalidState(allRules) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PushRule.isOnInvalidState(allRules: List<PushRule>): Boolean {
|
||||||
|
val parent = RuleIds.getParentRule(ruleId)?.let { parentId -> allRules.find { it.ruleId == parentId } }
|
||||||
|
val children = RuleIds.getSyncedRules(ruleId).mapNotNull { childId -> allRules.find { it.ruleId == childId } }
|
||||||
|
val isAlignedWithParent = parent?.let { isAlignedWithParentRule(it) }.orTrue()
|
||||||
|
return !isAlignedWithParent || !isAlignedWithChildrenRules(children)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PushRule.isAlignedWithParentRule(parent: PushRule) = this.getActions() == parent.getActions() && this.enabled == parent.enabled
|
||||||
|
private fun PushRule.isAlignedWithChildrenRules(children: List<PushRule>) = children.all { it.isAlignedWithParentRule(this) }
|
||||||
|
}
|
|
@ -24,18 +24,18 @@ import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class UpdatePushRulesIfNeededUseCase @Inject constructor() {
|
class UpdatePushRulesIfNeededUseCase @Inject constructor(
|
||||||
|
private val getPushRulesOnInvalidStateUseCase: GetPushRulesOnInvalidStateUseCase,
|
||||||
|
) {
|
||||||
|
|
||||||
suspend fun execute(session: Session) {
|
suspend fun execute(session: Session) {
|
||||||
|
val rulesOnError = getPushRulesOnInvalidStateUseCase.execute(session).takeUnless { it.isEmpty() } ?: return
|
||||||
|
|
||||||
val ruleSet = session.pushRuleService().getPushRules()
|
val ruleSet = session.pushRuleService().getPushRules()
|
||||||
val pushRules = ruleSet.getAllRules()
|
val rulesToUpdate = rulesOnError.mapNotNull { rule ->
|
||||||
val rulesToUpdate = pushRules.mapNotNull { rule ->
|
RuleIds.getParentRule(rule.ruleId)
|
||||||
val parent = RuleIds.getParentRule(rule.ruleId)?.let { ruleId -> ruleSet.findDefaultRule(ruleId) }
|
?.let { ruleId -> ruleSet.findDefaultRule(ruleId) }
|
||||||
if (parent != null && (rule.enabled != parent.pushRule.enabled || rule.actions != parent.pushRule.actions)) {
|
?.let { PushRuleWithParent(rule, it) }
|
||||||
PushRuleWithParent(rule, parent)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rulesToUpdate.forEach {
|
rulesToUpdate.forEach {
|
||||||
|
|
|
@ -25,4 +25,4 @@
|
||||||
android:title="@string/settings_encrypted_group_messages" />
|
android:title="@string/settings_encrypted_group_messages" />
|
||||||
|
|
||||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package im.vector.app.features.settings.notifications
|
package im.vector.app.features.settings.notifications
|
||||||
|
|
||||||
import com.airbnb.mvrx.test.MavericksTestRule
|
import com.airbnb.mvrx.test.MavericksTestRule
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.features.settings.notifications.usecase.GetPushRulesOnInvalidStateUseCase
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import im.vector.app.test.fixtures.PushRuleFixture
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
import im.vector.app.test.testDispatcher
|
import im.vector.app.test.testDispatcher
|
||||||
import io.mockk.coVerifyOrder
|
import io.mockk.coVerifyOrder
|
||||||
|
@ -32,25 +34,27 @@ import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.pushrules.RuleIds
|
import org.matrix.android.sdk.api.session.pushrules.RuleIds
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
|
||||||
|
|
||||||
internal class VectorSettingsPushRuleNotificationViewModelTest {
|
internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
||||||
|
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
private val fakeSession = FakeSession()
|
||||||
private val fakePushRuleService = fakeActiveSessionHolder.fakeSession.fakePushRuleService
|
private val fakePushRuleService = fakeSession.fakePushRuleService
|
||||||
|
private val fakeGetPushRulesOnInvalidStateUseCase = mockk<GetPushRulesOnInvalidStateUseCase>()
|
||||||
|
|
||||||
private val initialState = VectorSettingsPushRuleNotificationViewState()
|
private val initialState = VectorSettingsPushRuleNotificationViewState()
|
||||||
private fun createViewModel() = VectorSettingsPushRuleNotificationViewModel(
|
private fun createViewModel() = VectorSettingsPushRuleNotificationViewModel(
|
||||||
initialState = initialState,
|
initialState = initialState,
|
||||||
activeSessionHolder = fakeActiveSessionHolder.instance,
|
session = fakeSession,
|
||||||
|
fakeGetPushRulesOnInvalidStateUseCase,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockkStatic("im.vector.app.features.settings.notifications.NotificationIndexKt")
|
mockkStatic("im.vector.app.features.settings.notifications.NotificationIndexKt")
|
||||||
|
every { fakeGetPushRulesOnInvalidStateUseCase.execute(any()) } returns emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -65,12 +69,14 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
|
|
||||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||||
|
givenARuleId(firstRuleId)
|
||||||
|
givenARuleId(secondRuleId)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||||
|
|
||||||
// When
|
// When
|
||||||
val viewModelTest = viewModel.test()
|
val viewModelTest = viewModel.test()
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, true))
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, false))
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
|
@ -98,8 +104,8 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
{ copy(isLoading = false) },
|
{ copy(isLoading = false) },
|
||||||
)
|
)
|
||||||
.assertEvents(
|
.assertEvents(
|
||||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true),
|
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true),
|
||||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false),
|
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(secondRuleId, false),
|
||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -111,10 +117,12 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
val failure = mockk<Throwable>()
|
val failure = mockk<Throwable>()
|
||||||
|
|
||||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||||
|
givenARuleId(firstRuleId)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
||||||
|
|
||||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||||
|
givenARuleId(secondRuleId)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
||||||
|
@ -124,9 +132,9 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
// When
|
// When
|
||||||
val viewModelTest = viewModel.test()
|
val viewModelTest = viewModel.test()
|
||||||
// One rule failed to update
|
// One rule failed to update
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), true))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, true))
|
||||||
// All the rules failed to update
|
// All the rules failed to update
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), true))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, true))
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
|
@ -149,13 +157,13 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) },
|
{ copy(isLoading = false, rulesOnError = setOf(firstRuleId)) },
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) },
|
{ copy(isLoading = false) },
|
||||||
)
|
)
|
||||||
.assertEvents(
|
.assertEvents(
|
||||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure),
|
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true, failure),
|
||||||
VectorSettingsPushRuleNotificationViewEvent.Failure(failure),
|
VectorSettingsPushRuleNotificationViewEvent.Failure(secondRuleId, failure),
|
||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -167,10 +175,12 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
val failure = mockk<Throwable>()
|
val failure = mockk<Throwable>()
|
||||||
|
|
||||||
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
val firstRuleId = RuleIds.RULE_ID_ONE_TO_ONE_ROOM
|
||||||
|
givenARuleId(firstRuleId)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
fakePushRuleService.givenUpdatePushRuleActionsSucceed()
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, failure)
|
||||||
|
|
||||||
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
val secondRuleId = RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||||
|
givenARuleId(secondRuleId)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(secondRuleId, failure)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START, failure)
|
||||||
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
fakePushRuleService.givenUpdatePushRuleActionsFail(RuleIds.RULE_ID_POLL_START_UNSTABLE, failure)
|
||||||
|
@ -180,9 +190,9 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
// When
|
// When
|
||||||
val viewModelTest = viewModel.test()
|
val viewModelTest = viewModel.test()
|
||||||
// One rule failed to update
|
// One rule failed to update
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(firstRuleId), false))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(firstRuleId, false))
|
||||||
// All the rules failed to update
|
// All the rules failed to update
|
||||||
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(givenARuleId(secondRuleId), false))
|
viewModel.handle(VectorSettingsPushRuleNotificationViewAction.UpdatePushRule(secondRuleId, false))
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
|
@ -205,14 +215,14 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) },
|
{ copy(isLoading = false, rulesOnError = setOf(firstRuleId)) },
|
||||||
{ copy(isLoading = true) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(isLoading = false) },
|
{ copy(isLoading = false) },
|
||||||
)
|
)
|
||||||
.assertEvents(
|
.assertEvents(
|
||||||
// The global rule remains checked if all the rules are not unchecked
|
// The global rule remains checked if all the rules are not unchecked
|
||||||
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, failure),
|
VectorSettingsPushRuleNotificationViewEvent.PushRuleUpdated(firstRuleId, true, failure),
|
||||||
VectorSettingsPushRuleNotificationViewEvent.Failure(failure),
|
VectorSettingsPushRuleNotificationViewEvent.Failure(secondRuleId, failure),
|
||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
@ -244,15 +254,11 @@ internal class VectorSettingsPushRuleNotificationViewModelTest {
|
||||||
secondResult shouldBe false
|
secondResult shouldBe false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun givenARuleId(ruleId: String, notificationIndex: NotificationIndex = NotificationIndex.NOISY): PushRuleAndKind {
|
private fun givenARuleId(ruleId: String, notificationIndex: NotificationIndex = NotificationIndex.NOISY) {
|
||||||
val ruleAndKind = mockk<PushRuleAndKind> {
|
val pushRule = PushRuleFixture.aPushRule(ruleId)
|
||||||
every { pushRule.ruleId } returns ruleId
|
every { pushRule.notificationIndex } returns notificationIndex
|
||||||
every { pushRule.notificationIndex } returns notificationIndex
|
val ruleAndKind = PushRuleFixture.aPushRuleAndKind(pushRule)
|
||||||
every { kind } returns mockk()
|
|
||||||
}
|
|
||||||
|
|
||||||
every { fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
every { fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
||||||
|
|
||||||
return ruleAndKind
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.settings.notifications.usecase
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import im.vector.app.test.fixtures.PushRuleFixture
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
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.rest.PushRule
|
||||||
|
|
||||||
|
internal class GetPushRulesOnInvalidStateUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeSession = FakeSession()
|
||||||
|
private val getPushRulesOnInvalidStateUseCase = GetPushRulesOnInvalidStateUseCase()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
mockkStatic("org.matrix.android.sdk.api.session.pushrules.ActionKt")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given a list of push rules with children not matching their parent when execute then returns the list of not matching rules`() {
|
||||||
|
// Given
|
||||||
|
val firstActions = listOf(Action.Notify)
|
||||||
|
val secondActions = listOf(Action.DoNotNotify)
|
||||||
|
givenARuleList(
|
||||||
|
listOf(
|
||||||
|
// first set of related rules
|
||||||
|
givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, firstActions),
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, true, listOf(Action.Notify)),
|
||||||
|
// second set of related rules
|
||||||
|
givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false, secondActions),
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_START_UNSTABLE, false, listOf(Action.DoNotNotify)),
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff
|
||||||
|
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff
|
||||||
|
// Another rule
|
||||||
|
givenARuleId(RuleIds.RULE_ID_CONTAIN_USER_NAME, true, listOf(Action.Notify)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val result = getPushRulesOnInvalidStateUseCase.execute(fakeSession).map { it.ruleId }
|
||||||
|
|
||||||
|
// Then
|
||||||
|
result shouldBeEqualTo listOf(
|
||||||
|
RuleIds.RULE_ID_ONE_TO_ONE_ROOM, // parent rule
|
||||||
|
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_ALL_OTHER_MESSAGES_ROOMS, // parent rule
|
||||||
|
RuleIds.RULE_ID_POLL_START,
|
||||||
|
RuleIds.RULE_ID_POLL_END,
|
||||||
|
RuleIds.RULE_ID_POLL_END_UNSTABLE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenARuleList(rules: List<PushRule>) {
|
||||||
|
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rules
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
|
||||||
|
val ruleAndKind = PushRuleFixture.aPushRuleAndKind(
|
||||||
|
PushRuleFixture.aPushRule(ruleId, enabled, actions),
|
||||||
|
)
|
||||||
|
|
||||||
|
every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
||||||
|
|
||||||
|
return ruleAndKind.pushRule
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.settings.notifications.usecase
|
package im.vector.app.features.settings.notifications.usecase
|
||||||
|
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import im.vector.app.test.fixtures.PushRuleFixture
|
||||||
import io.mockk.coVerifySequence
|
import io.mockk.coVerifySequence
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
@ -28,14 +29,13 @@ import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.pushrules.Action
|
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.RuleIds
|
||||||
import org.matrix.android.sdk.api.session.pushrules.getActions
|
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
|
||||||
|
|
||||||
internal class UpdatePushRulesIfNeededUseCaseTest {
|
internal class UpdatePushRulesIfNeededUseCaseTest {
|
||||||
|
|
||||||
private val fakeSession = FakeSession()
|
private val fakeSession = FakeSession()
|
||||||
private val updatePushRulesIfNeededUseCase = UpdatePushRulesIfNeededUseCase()
|
private val fakeGetPushRulesOnInvalidStateUseCase = mockk<GetPushRulesOnInvalidStateUseCase>()
|
||||||
|
private val updatePushRulesIfNeededUseCase = UpdatePushRulesIfNeededUseCase(fakeGetPushRulesOnInvalidStateUseCase)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
@ -50,25 +50,26 @@ internal class UpdatePushRulesIfNeededUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun test() = runTest {
|
fun test() = runTest {
|
||||||
// Given
|
// Given
|
||||||
val firstActions = listOf(Action.Notify)
|
val firstParentEnabled = true
|
||||||
val secondActions = listOf(Action.DoNotNotify)
|
val firstParentActions = listOf(Action.Notify)
|
||||||
val rules = listOf(
|
val firstParent = givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, firstParentEnabled, firstParentActions)
|
||||||
|
val secondParentEnabled = false
|
||||||
|
val secondParentActions = listOf(Action.DoNotNotify)
|
||||||
|
val secondParent = givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, secondParentEnabled, secondParentActions)
|
||||||
|
val rulesOnError = listOf(
|
||||||
// first set of related rules
|
// first set of related rules
|
||||||
givenARuleId(RuleIds.RULE_ID_ONE_TO_ONE_ROOM, true, firstActions),
|
firstParent,
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, listOf(Action.DoNotNotify)), // diff
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, emptyList()), // diff
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, false, listOf(Action.Notify)), // diff
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_END_ONE_TO_ONE_UNSTABLE, true, listOf(Action.Notify)),
|
|
||||||
// second set of related rules
|
// second set of related rules
|
||||||
givenARuleId(RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS, false, secondActions),
|
secondParent,
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_START, true, listOf(Action.Notify)), // diff
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_START_UNSTABLE, false, listOf(Action.DoNotNotify)),
|
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_END, false, listOf(Action.Notify)), // diff
|
||||||
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff
|
givenARuleId(RuleIds.RULE_ID_POLL_END_UNSTABLE, true, listOf()), // diff
|
||||||
// Another rule
|
|
||||||
givenARuleId(RuleIds.RULE_ID_CONTAIN_USER_NAME, true, listOf(Action.Notify)),
|
|
||||||
)
|
)
|
||||||
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rules
|
every { fakeGetPushRulesOnInvalidStateUseCase.execute(fakeSession) } returns rulesOnError
|
||||||
|
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rulesOnError
|
||||||
|
|
||||||
// When
|
// When
|
||||||
updatePushRulesIfNeededUseCase.execute(fakeSession)
|
updatePushRulesIfNeededUseCase.execute(fakeSession)
|
||||||
|
@ -77,30 +78,23 @@ internal class UpdatePushRulesIfNeededUseCaseTest {
|
||||||
coVerifySequence {
|
coVerifySequence {
|
||||||
fakeSession.fakePushRuleService.getPushRules()
|
fakeSession.fakePushRuleService.getPushRules()
|
||||||
// first set
|
// first set
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, true, firstActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE, firstParentEnabled, firstParentActions)
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, true, firstActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START_ONE_TO_ONE_UNSTABLE, firstParentEnabled, firstParentActions)
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, true, firstActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, firstParentEnabled, firstParentActions)
|
||||||
// second set
|
// second set
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, false, secondActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, secondParentEnabled, secondParentActions)
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, false, secondActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, secondParentEnabled, secondParentActions)
|
||||||
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, false, secondActions)
|
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, secondParentEnabled, secondParentActions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
|
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
|
||||||
val pushRule = mockk<PushRule> {
|
val ruleAndKind = PushRuleFixture.aPushRuleAndKind(
|
||||||
every { this@mockk.ruleId } returns ruleId
|
pushRule = PushRuleFixture.aPushRule(ruleId, enabled, actions),
|
||||||
every { this@mockk.enabled } returns enabled
|
)
|
||||||
every { this@mockk.actions } returns actions
|
|
||||||
every { getActions() } returns actions
|
|
||||||
}
|
|
||||||
val ruleAndKind = mockk<PushRuleAndKind> {
|
|
||||||
every { this@mockk.pushRule } returns pushRule
|
|
||||||
every { kind } returns mockk()
|
|
||||||
}
|
|
||||||
|
|
||||||
every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
every { fakeSession.fakePushRuleService.getPushRules().findDefaultRule(ruleId) } returns ruleAndKind
|
||||||
|
|
||||||
return pushRule
|
return ruleAndKind.pushRule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fixtures
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.Action
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.RuleSetKey
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.getActions
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
|
||||||
|
|
||||||
|
object PushRuleFixture {
|
||||||
|
|
||||||
|
private const val A_RULE_ID = "a-rule-id"
|
||||||
|
|
||||||
|
// Needs: mockkStatic("org.matrix.android.sdk.api.session.pushrules.ActionKt")
|
||||||
|
fun aPushRule(
|
||||||
|
ruleId: String = A_RULE_ID,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
actions: List<Action> = listOf(Action.Notify),
|
||||||
|
): PushRule = mockk {
|
||||||
|
every { this@mockk.ruleId } returns ruleId
|
||||||
|
every { this@mockk.enabled } returns enabled
|
||||||
|
every { getActions() } returns actions
|
||||||
|
}
|
||||||
|
|
||||||
|
fun aPushRuleAndKind(
|
||||||
|
pushRule: PushRule = aPushRule(),
|
||||||
|
kind: RuleSetKey = RuleSetKey.UNDERRIDE,
|
||||||
|
): PushRuleAndKind = mockk {
|
||||||
|
every { this@mockk.pushRule } returns pushRule
|
||||||
|
every { this@mockk.kind } returns kind
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue