Merge pull request #8130 from vector-im/feature/fre/poll_sync_push_rules_after_creation

[Poll] Synchronize polls and message push rules after creation (PSG-1137)
This commit is contained in:
Florian Renaud 2023-02-17 13:46:13 +01:00 committed by GitHub
commit 7d16c86f37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 317 additions and 33 deletions

1
changelog.d/8130.feature Normal file
View File

@ -0,0 +1 @@
[Poll] Synchronize polls and message push rules

View File

@ -61,22 +61,4 @@ object RuleIds {
const val RULE_ID_FALLBACK = ".m.rule.fallback"
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

@ -0,0 +1,54 @@
/*
* 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.core.notification
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.notifications.usecase.UpdatePushRulesIfNeededUseCase
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.flow.flow
import javax.inject.Inject
import javax.inject.Singleton
/**
* Listen changes in Account Data to update the push rules if needed.
*/
@Singleton
class PushRulesUpdater @Inject constructor(
private val updatePushRulesIfNeededUseCase: UpdatePushRulesIfNeededUseCase,
) {
private var job: Job? = null
fun onSessionStarted(session: Session) {
updatePushRulesOnChange(session)
}
private fun updatePushRulesOnChange(session: Session) {
job?.cancel()
job = session.coroutineScope.launch {
session.flow()
.liveUserAccountData(UserAccountDataTypes.TYPE_PUSH_RULES)
.onEach { updatePushRulesIfNeededUseCase.execute(session) }
.collect()
}
}
}

View File

@ -20,6 +20,7 @@ import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.notification.NotificationsSettingUpdater
import im.vector.app.core.notification.PushRulesUpdater
import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.session.coroutineScope
@ -37,6 +38,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
private val vectorPreferences: VectorPreferences,
private val notificationsSettingUpdater: NotificationsSettingUpdater,
private val updateNotificationSettingsAccountDataUseCase: UpdateNotificationSettingsAccountDataUseCase,
private val pushRulesUpdater: PushRulesUpdater,
) {
fun execute(session: Session, startSyncing: Boolean = true) {
@ -50,6 +52,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
updateMatrixClientInfoIfNeeded(session)
createNotificationSettingsAccountDataIfNeeded(session)
notificationsSettingUpdater.onSessionStarted(session)
pushRulesUpdater.onSessionStarted(session)
}
private fun updateMatrixClientInfoIfNeeded(session: Session) {

View File

@ -0,0 +1,51 @@
/*
* 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
import org.matrix.android.sdk.api.session.pushrules.RuleIds
fun RuleIds.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()
}
}
fun RuleIds.getParentRule(ruleId: String): String? {
return when (ruleId) {
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_ONE_TO_ONE_ROOM
RULE_ID_POLL_START,
RULE_ID_POLL_START_UNSTABLE,
RULE_ID_POLL_END,
RULE_ID_POLL_END_UNSTABLE -> RULE_ID_ALL_OTHER_MESSAGES_ROOMS
else -> null
}
}

View File

@ -0,0 +1,55 @@
/*
* 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 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 org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
import javax.inject.Inject
class UpdatePushRulesIfNeededUseCase @Inject constructor() {
suspend fun execute(session: Session) {
val ruleSet = session.pushRuleService().getPushRules()
val pushRules = ruleSet.getAllRules()
val rulesToUpdate = pushRules.mapNotNull { rule ->
val parent = RuleIds.getParentRule(rule.ruleId)?.let { ruleId -> ruleSet.findDefaultRule(ruleId) }
if (parent != null && (rule.enabled != parent.pushRule.enabled || rule.actions != parent.pushRule.actions)) {
PushRuleWithParent(rule, parent)
} else {
null
}
}
rulesToUpdate.forEach {
session.pushRuleService().updatePushRuleActions(
kind = it.parent.kind,
ruleId = it.rule.ruleId,
enable = it.parent.pushRule.enabled,
actions = it.parent.pushRule.getActions(),
)
}
}
private data class PushRuleWithParent(
val rule: PushRule,
val parent: PushRuleAndKind,
)
}

View File

@ -22,6 +22,7 @@ import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.devices.v2.notification.UpdateNotificationSettingsAccountDataUseCase
import im.vector.app.test.fakes.FakeContext
import im.vector.app.test.fakes.FakeNotificationsSettingUpdater
import im.vector.app.test.fakes.FakePushRulesUpdater
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.FakeVectorPreferences
import im.vector.app.test.fakes.FakeWebRtcCallManager
@ -47,6 +48,7 @@ class ConfigureAndStartSessionUseCaseTest {
private val fakeUpdateMatrixClientInfoUseCase = mockk<UpdateMatrixClientInfoUseCase>()
private val fakeVectorPreferences = FakeVectorPreferences()
private val fakeNotificationsSettingUpdater = FakeNotificationsSettingUpdater()
private val fakePushRulesUpdater = FakePushRulesUpdater()
private val fakeUpdateNotificationSettingsAccountDataUseCase = mockk<UpdateNotificationSettingsAccountDataUseCase>()
private val configureAndStartSessionUseCase = ConfigureAndStartSessionUseCase(
@ -56,6 +58,7 @@ class ConfigureAndStartSessionUseCaseTest {
vectorPreferences = fakeVectorPreferences.instance,
notificationsSettingUpdater = fakeNotificationsSettingUpdater.instance,
updateNotificationSettingsAccountDataUseCase = fakeUpdateNotificationSettingsAccountDataUseCase,
pushRulesUpdater = fakePushRulesUpdater.instance,
)
@Before
@ -78,7 +81,8 @@ class ConfigureAndStartSessionUseCaseTest {
coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession)
fakeNotificationsSettingUpdater.givenOnSessionStarted(aSession)
fakePushRulesUpdater.givenOnSessionStarted(aSession)
// When
configureAndStartSessionUseCase.execute(aSession, startSyncing = true)
@ -102,7 +106,8 @@ class ConfigureAndStartSessionUseCaseTest {
fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = false)
fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession)
fakeNotificationsSettingUpdater.givenOnSessionStarted(aSession)
fakePushRulesUpdater.givenOnSessionStarted(aSession)
// When
configureAndStartSessionUseCase.execute(aSession, startSyncing = true)
@ -129,7 +134,8 @@ class ConfigureAndStartSessionUseCaseTest {
coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
coJustRun { fakeUpdateNotificationSettingsAccountDataUseCase.execute(any()) }
fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
fakeNotificationsSettingUpdater.givenOnSessionsStarted(aSession)
fakeNotificationsSettingUpdater.givenOnSessionStarted(aSession)
fakePushRulesUpdater.givenOnSessionStarted(aSession)
// When
configureAndStartSessionUseCase.execute(aSession, startSyncing = false)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@ -14,11 +14,9 @@
* limitations under the License.
*/
package im.vector.app.features.settings.notifications
package im.vector.app.features.settings.notifications.usecase
import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase
import im.vector.app.features.settings.notifications.usecase.DisableNotificationsForCurrentSessionUseCase
import im.vector.app.features.settings.notifications.usecase.ToggleNotificationsForCurrentSessionUseCase
import im.vector.app.test.fakes.FakePushersManager
import io.mockk.coJustRun
import io.mockk.coVerify

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@ -14,12 +14,10 @@
* limitations under the License.
*/
package im.vector.app.features.settings.notifications
package im.vector.app.features.settings.notifications.usecase
import im.vector.app.core.pushers.EnsureFcmTokenIsRetrievedUseCase
import im.vector.app.core.pushers.RegisterUnifiedPushUseCase
import im.vector.app.features.settings.notifications.usecase.EnableNotificationsForCurrentSessionUseCase
import im.vector.app.features.settings.notifications.usecase.ToggleNotificationsForCurrentSessionUseCase
import im.vector.app.test.fakes.FakePushersManager
import io.mockk.coJustRun
import io.mockk.coVerify

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* 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.
@ -14,12 +14,11 @@
* limitations under the License.
*/
package im.vector.app.features.settings.notifications
package im.vector.app.features.settings.notifications.usecase
import im.vector.app.features.settings.devices.v2.notification.CheckIfCanToggleNotificationsViaPusherUseCase
import im.vector.app.features.settings.devices.v2.notification.DeleteNotificationSettingsAccountDataUseCase
import im.vector.app.features.settings.devices.v2.notification.SetNotificationSettingsAccountDataUseCase
import im.vector.app.features.settings.notifications.usecase.ToggleNotificationsForCurrentSessionUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeUnifiedPushHelper
import im.vector.app.test.fixtures.PusherFixture

View File

@ -0,0 +1,106 @@
/*
* 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 io.mockk.coVerifySequence
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import kotlinx.coroutines.test.runTest
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.getActions
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
import org.matrix.android.sdk.api.session.pushrules.rest.PushRuleAndKind
internal class UpdatePushRulesIfNeededUseCaseTest {
private val fakeSession = FakeSession()
private val updatePushRulesIfNeededUseCase = UpdatePushRulesIfNeededUseCase()
@Before
fun setup() {
mockkStatic("org.matrix.android.sdk.api.session.pushrules.ActionKt")
}
@After
fun tearDown() {
unmockkAll()
}
@Test
fun test() = runTest {
// Given
val firstActions = listOf(Action.Notify)
val secondActions = listOf(Action.DoNotNotify)
val rules = 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)),
)
every { fakeSession.fakePushRuleService.getPushRules().getAllRules() } returns rules
// When
updatePushRulesIfNeededUseCase.execute(fakeSession)
// Then
coVerifySequence {
fakeSession.fakePushRuleService.getPushRules()
// 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_UNSTABLE, true, firstActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_ONE_TO_ONE, true, firstActions)
// second set
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_START, false, secondActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END, false, secondActions)
fakeSession.fakePushRuleService.updatePushRuleActions(any(), RuleIds.RULE_ID_POLL_END_UNSTABLE, false, secondActions)
}
}
private fun givenARuleId(ruleId: String, enabled: Boolean, actions: List<Action>): PushRule {
val pushRule = mockk<PushRule> {
every { this@mockk.ruleId } returns ruleId
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
return pushRule
}
}

View File

@ -25,7 +25,7 @@ class FakeNotificationsSettingUpdater {
val instance = mockk<NotificationsSettingUpdater>()
fun givenOnSessionsStarted(session: Session) {
fun givenOnSessionStarted(session: Session) {
justRun { instance.onSessionStarted(session) }
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 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.fakes
import im.vector.app.core.notification.PushRulesUpdater
import io.mockk.justRun
import io.mockk.mockk
import org.matrix.android.sdk.api.session.Session
class FakePushRulesUpdater {
val instance = mockk<PushRulesUpdater>()
fun givenOnSessionStarted(session: Session) {
justRun { instance.onSessionStarted(session) }
}
}