Merge pull request #5772 from vector-im/feature/bma/ignore_users

Delete Events from ignored users and trigger a clear cache request dialog when unignoring user(s)
This commit is contained in:
Benoit Marty 2022-04-21 11:03:09 +02:00 committed by GitHub
commit 7f3e72b9cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 132 additions and 96 deletions

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

@ -0,0 +1 @@
Improve management of ignored users

View File

@ -23,5 +23,10 @@ sealed class GlobalError {
data class InvalidToken(val softLogout: Boolean) : GlobalError() data class InvalidToken(val softLogout: Boolean) : GlobalError()
data class ConsentNotGivenError(val consentUri: String) : GlobalError() data class ConsentNotGivenError(val consentUri: String) : GlobalError()
data class CertificateError(val fingerprint: Fingerprint) : GlobalError() data class CertificateError(val fingerprint: Fingerprint) : GlobalError()
/**
* The SDK requires the app (which should request the user) to perform an initial sync.
*/
data class InitialSyncRequest(val reason: InitialSyncRequestReason) : GlobalError()
object ExpiredAccount : GlobalError() object ExpiredAccount : GlobalError()
} }

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.failure
/**
* This enum provide the reason why the SDK request an initial sync to the application
*/
enum class InitialSyncRequestReason {
/**
* The list of ignored users has changed, and at least one user who was ignored is not ignored anymore
*/
IGNORED_USERS_LIST_CHANGE,
}

View File

@ -298,6 +298,7 @@ interface Session :
* Possible cases: * Possible cases:
* - The access token is not valid anymore, * - The access token is not valid anymore,
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver * - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
* See [GlobalError] for all the possible cases
*/ */
fun onGlobalError(session: Session, globalError: GlobalError) = Unit fun onGlobalError(session: Session, globalError: GlobalError) = Unit
} }

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.api.session.room.timeline package org.matrix.android.sdk.api.session.room.timeline
// TODO Move to internal, strange?
data class TimelineEventFilters( data class TimelineEventFilters(
/** /**
* A flag to filter edit events * A flag to filter edit events

View File

@ -31,7 +31,8 @@ data class TimelineSettings(
/** /**
* The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline * The root thread eventId if this is a thread timeline, or null if this is NOT a thread timeline
*/ */
val rootThreadEventId: String? = null) { val rootThreadEventId: String? = null,
) {
/** /**
* Returns true if this is a thread timeline or false otherwise * Returns true if this is a thread timeline or false otherwise

View File

@ -100,6 +100,7 @@ internal class DefaultTimeline(private val roomId: String,
threadsAwarenessHandler = threadsAwarenessHandler, threadsAwarenessHandler = threadsAwarenessHandler,
lightweightSettingsStorage = lightweightSettingsStorage, lightweightSettingsStorage = lightweightSettingsStorage,
onEventsUpdated = this::sendSignalToPostSnapshot, onEventsUpdated = this::sendSignalToPostSnapshot,
onEventsDeleted = this::onEventsDeleted,
onLimitedTimeline = this::onLimitedTimeline, onLimitedTimeline = this::onLimitedTimeline,
onNewTimelineEvents = this::onNewTimelineEvents onNewTimelineEvents = this::onNewTimelineEvents
) )
@ -304,6 +305,12 @@ internal class DefaultTimeline(private val roomId: String,
} }
} }
private fun onEventsDeleted() {
// Some event have been deleted, for instance when a user has been ignored.
// Restart the timeline (live)
restartWithEventId(null)
}
private suspend fun postSnapshot() { private suspend fun postSnapshot() {
val snapshot = strategy.buildSnapshot() val snapshot = strategy.buildSnapshot()
Timber.v("Post snapshot of ${snapshot.size} events") Timber.v("Post snapshot of ${snapshot.size} events")

View File

@ -95,6 +95,7 @@ internal class LoadTimelineStrategy(
val threadsAwarenessHandler: ThreadsAwarenessHandler, val threadsAwarenessHandler: ThreadsAwarenessHandler,
val lightweightSettingsStorage: LightweightSettingsStorage, val lightweightSettingsStorage: LightweightSettingsStorage,
val onEventsUpdated: (Boolean) -> Unit, val onEventsUpdated: (Boolean) -> Unit,
val onEventsDeleted: () -> Unit,
val onLimitedTimeline: () -> Unit, val onLimitedTimeline: () -> Unit,
val onNewTimelineEvents: (List<String>) -> Unit val onNewTimelineEvents: (List<String>) -> Unit
) )
@ -302,7 +303,8 @@ internal class LoadTimelineStrategy(
threadsAwarenessHandler = dependencies.threadsAwarenessHandler, threadsAwarenessHandler = dependencies.threadsAwarenessHandler,
lightweightSettingsStorage = dependencies.lightweightSettingsStorage, lightweightSettingsStorage = dependencies.lightweightSettingsStorage,
initialEventId = mode.originEventId(), initialEventId = mode.originEventId(),
onBuiltEvents = dependencies.onEventsUpdated onBuiltEvents = dependencies.onEventsUpdated,
onEventsDeleted = dependencies.onEventsDeleted,
) )
} }
} }

View File

@ -59,11 +59,13 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
private val realmConfiguration: RealmConfiguration, private val realmConfiguration: RealmConfiguration,
private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask, private val fetchTokenAndPaginateTask: FetchTokenAndPaginateTask,
private val timelineEventMapper: TimelineEventMapper, private val timelineEventMapper: TimelineEventMapper,
private val uiEchoManager: UIEchoManager? = null, private val uiEchoManager: UIEchoManager?,
private val threadsAwarenessHandler: ThreadsAwarenessHandler, private val threadsAwarenessHandler: ThreadsAwarenessHandler,
private val lightweightSettingsStorage: LightweightSettingsStorage, private val lightweightSettingsStorage: LightweightSettingsStorage,
private val initialEventId: String?, private val initialEventId: String?,
private val onBuiltEvents: (Boolean) -> Unit) { private val onBuiltEvents: (Boolean) -> Unit,
private val onEventsDeleted: () -> Unit,
) {
private val isLastForward = AtomicBoolean(chunkEntity.isLastForward) private val isLastForward = AtomicBoolean(chunkEntity.isLastForward)
private val isLastBackward = AtomicBoolean(chunkEntity.isLastBackward) private val isLastBackward = AtomicBoolean(chunkEntity.isLastBackward)
@ -505,6 +507,11 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
if (insertions.isNotEmpty() || modifications.isNotEmpty()) { if (insertions.isNotEmpty() || modifications.isNotEmpty()) {
onBuiltEvents(true) onBuiltEvents(true)
} }
val deletions = changeSet.deletions
if (deletions.isNotEmpty()) {
onEventsDeleted()
}
} }
private fun getNextDisplayIndex(direction: Timeline.Direction): Int? { private fun getNextDisplayIndex(direction: Timeline.Direction): Int? {
@ -543,7 +550,8 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
threadsAwarenessHandler = threadsAwarenessHandler, threadsAwarenessHandler = threadsAwarenessHandler,
lightweightSettingsStorage = lightweightSettingsStorage, lightweightSettingsStorage = lightweightSettingsStorage,
initialEventId = null, initialEventId = null,
onBuiltEvents = this.onBuiltEvents onBuiltEvents = this.onBuiltEvents,
onEventsDeleted = this.onEventsDeleted
) )
} }

View File

@ -20,6 +20,8 @@ import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.kotlin.where import io.realm.kotlin.where
import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.failure.InitialSyncRequestReason
import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.pushrules.RuleScope
import org.matrix.android.sdk.api.pushrules.RuleSetKey import org.matrix.android.sdk.api.pushrules.RuleSetKey
import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
@ -31,6 +33,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.sync.model.InvitedRoomSync import org.matrix.android.sdk.api.session.sync.model.InvitedRoomSync
import org.matrix.android.sdk.api.session.sync.model.UserAccountDataSync import org.matrix.android.sdk.api.session.sync.model.UserAccountDataSync
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
@ -39,6 +42,8 @@ import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity
import org.matrix.android.sdk.internal.database.model.PushRulesEntity import org.matrix.android.sdk.internal.database.model.PushRulesEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.database.model.deleteOnCascade
@ -46,7 +51,10 @@ import org.matrix.android.sdk.internal.database.query.getDirectRooms
import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrCreate
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionListeners
import org.matrix.android.sdk.internal.session.dispatchTo
import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
@ -65,7 +73,10 @@ internal class UserAccountDataSyncHandler @Inject constructor(
private val directChatsHelper: DirectChatsHelper, private val directChatsHelper: DirectChatsHelper,
private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val roomAvatarResolver: RoomAvatarResolver, private val roomAvatarResolver: RoomAvatarResolver,
private val roomDisplayNameResolver: RoomDisplayNameResolver private val roomDisplayNameResolver: RoomDisplayNameResolver,
@SessionId private val sessionId: String,
private val sessionManager: SessionManager,
private val sessionListeners: SessionListeners
) { ) {
fun handle(realm: Realm, accountData: UserAccountDataSync?) { fun handle(realm: Realm, accountData: UserAccountDataSync?) {
@ -184,12 +195,39 @@ internal class UserAccountDataSyncHandler @Inject constructor(
private fun handleIgnoredUsers(realm: Realm, event: UserAccountDataEvent) { private fun handleIgnoredUsers(realm: Realm, event: UserAccountDataEvent) {
val userIds = event.content.toModel<IgnoredUsersContent>()?.ignoredUsers?.keys ?: return val userIds = event.content.toModel<IgnoredUsersContent>()?.ignoredUsers?.keys ?: return
realm.where(IgnoredUserEntity::class.java) val currentIgnoredUsers = realm.where(IgnoredUserEntity::class.java).findAll()
.findAll() val currentIgnoredUserIds = currentIgnoredUsers.map { it.userId }
.deleteAllFromRealm() // Delete the previous list
currentIgnoredUsers.deleteAllFromRealm()
// And save the new received list // And save the new received list
userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } } userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
// TODO If not initial sync, we should execute a init sync
// Delete all the TimelineEvents for all the ignored users
// See https://spec.matrix.org/latest/client-server-api/#client-behaviour-22 :
// "Once ignored, the client will no longer receive events sent by that user, with the exception of state events"
// So just delete all non-state events from our local storage.
realm.where(TimelineEventEntity::class.java)
.`in`(TimelineEventEntityFields.ROOT.SENDER, userIds.toTypedArray())
.isNull(TimelineEventEntityFields.ROOT.STATE_KEY)
.findAll()
.also { Timber.d("Deleting ${it.size} TimelineEventEntity from ignored users") }
.forEach {
it.deleteOnCascade(true)
}
// Handle the case when some users are unignored from another session
val mustRefreshCache = currentIgnoredUserIds.any { currentIgnoredUserId -> currentIgnoredUserId !in userIds }
if (mustRefreshCache) {
Timber.d("A user has been unignored from another session, an initial sync should be performed")
dispatchMustRefresh()
}
}
private fun dispatchMustRefresh() {
val session = sessionManager.getSessionComponent(sessionId)?.session()
session.dispatchTo(sessionListeners) { safeSession, listener ->
listener.onGlobalError(safeSession, GlobalError.InitialSyncRequest(InitialSyncRequestReason.IGNORED_USERS_LIST_CHANGE))
}
} }
private fun handleBreadcrumbs(realm: Realm, event: UserAccountDataEvent) { private fun handleBreadcrumbs(realm: Realm, event: UserAccountDataEvent) {

View File

@ -21,9 +21,7 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import org.matrix.android.sdk.api.session.user.UserService import org.matrix.android.sdk.api.session.user.UserService
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.user.accountdata.DefaultSaveIgnoredUsersTask
import org.matrix.android.sdk.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask import org.matrix.android.sdk.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask
import org.matrix.android.sdk.internal.session.user.accountdata.SaveIgnoredUsersTask
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateIgnoredUserIdsTask import org.matrix.android.sdk.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
import org.matrix.android.sdk.internal.session.user.model.DefaultSearchUserTask import org.matrix.android.sdk.internal.session.user.model.DefaultSearchUserTask
import org.matrix.android.sdk.internal.session.user.model.SearchUserTask import org.matrix.android.sdk.internal.session.user.model.SearchUserTask
@ -48,9 +46,6 @@ internal abstract class UserModule {
@Binds @Binds
abstract fun bindSearchUserTask(task: DefaultSearchUserTask): SearchUserTask abstract fun bindSearchUserTask(task: DefaultSearchUserTask): SearchUserTask
@Binds
abstract fun bindSaveIgnoredUsersTask(task: DefaultSaveIgnoredUsersTask): SaveIgnoredUsersTask
@Binds @Binds
abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask

View File

@ -1,47 +0,0 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.session.user.accountdata
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import javax.inject.Inject
/**
* Save the ignored users list in DB
*/
internal interface SaveIgnoredUsersTask : Task<SaveIgnoredUsersTask.Params, Unit> {
data class Params(
val userIds: List<String>
)
}
internal class DefaultSaveIgnoredUsersTask @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : SaveIgnoredUsersTask {
override suspend fun execute(params: SaveIgnoredUsersTask.Params) {
monarchy.awaitTransaction { realm ->
// clear current ignored users
realm.where(IgnoredUserEntity::class.java)
.findAll()
.deleteAllFromRealm()
// And save the new received list
params.userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
}
}
}

View File

@ -38,7 +38,6 @@ internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Para
internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor( internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(
private val accountDataApi: AccountDataAPI, private val accountDataApi: AccountDataAPI,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
@UserId private val userId: String, @UserId private val userId: String,
private val globalErrorReceiver: GlobalErrorReceiver private val globalErrorReceiver: GlobalErrorReceiver
) : UpdateIgnoredUserIdsTask { ) : UpdateIgnoredUserIdsTask {
@ -66,8 +65,5 @@ internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(
executeRequest(globalErrorReceiver) { executeRequest(globalErrorReceiver) {
accountDataApi.setAccountData(userId, UserAccountDataTypes.TYPE_IGNORED_USER_LIST, body) accountDataApi.setAccountData(userId, UserAccountDataTypes.TYPE_IGNORED_USER_LIST, body)
} }
// Update the DB right now (do not wait for the sync to come back with updated data, for a faster UI update)
saveIgnoredUsersTask.execute(SaveIgnoredUsersTask.Params(list))
} }
} }

View File

@ -46,6 +46,7 @@ import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.MavericksView import com.airbnb.mvrx.MavericksView
import com.bumptech.glide.util.Util import com.bumptech.glide.util.Util
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.EntryPointAccessors import dagger.hilt.android.EntryPointAccessors
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
@ -86,6 +87,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.failure.InitialSyncRequestReason
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -266,9 +268,27 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
is GlobalError.CertificateError -> is GlobalError.CertificateError ->
handleCertificateError(globalError) handleCertificateError(globalError)
GlobalError.ExpiredAccount -> Unit // TODO Handle account expiration GlobalError.ExpiredAccount -> Unit // TODO Handle account expiration
is GlobalError.InitialSyncRequest -> handleInitialSyncRequest(globalError)
} }
} }
private fun handleInitialSyncRequest(initialSyncRequest: GlobalError.InitialSyncRequest) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.initial_sync_request_title)
.setMessage(
getString(R.string.initial_sync_request_content, getString(
when (initialSyncRequest.reason) {
InitialSyncRequestReason.IGNORED_USERS_LIST_CHANGE -> R.string.initial_sync_request_reason_unignored_users
}
))
)
.setPositiveButton(R.string.ok) { _, _ ->
MainActivity.restartApp(this, MainActivityArgs(clearCache = true))
}
.setNegativeButton(R.string.later, null)
.show()
}
private fun handleCertificateError(certificateError: GlobalError.CertificateError) { private fun handleCertificateError(certificateError: GlobalError.CertificateError) {
singletonEntryPoint() singletonEntryPoint()
.unrecognizedCertificateDialog() .unrecognizedCertificateDialog()

View File

@ -119,8 +119,6 @@ import im.vector.app.core.utils.startInstallFromSourceIntent
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.DialogReportContentBinding import im.vector.app.databinding.DialogReportContentBinding
import im.vector.app.databinding.FragmentTimelineBinding import im.vector.app.databinding.FragmentTimelineBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
import im.vector.app.features.analytics.plan.Interaction import im.vector.app.features.analytics.plan.Interaction
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
@ -1730,14 +1728,10 @@ class TimelineFragment @Inject constructor(
dismissLoadingDialog() dismissLoadingDialog()
views.composerLayout.setTextIfDifferent("") views.composerLayout.setTextIfDifferent("")
when (parsedCommand) { when (parsedCommand) {
is ParsedCommand.SetMarkdown -> { is ParsedCommand.SetMarkdown -> {
showSnackWithMessage(getString(if (parsedCommand.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled)) showSnackWithMessage(getString(if (parsedCommand.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled))
} }
is ParsedCommand.UnignoreUser -> { else -> Unit
// A user has been un-ignored, perform a initial sync
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = true))
}
else -> Unit
} }
} }

View File

@ -47,8 +47,6 @@ import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.DialogShareQrCodeBinding import im.vector.app.databinding.DialogShareQrCodeBinding
import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
@ -133,20 +131,13 @@ class RoomMemberProfileFragment @Inject constructor(
is RoomMemberProfileViewEvents.OnKickActionSuccess -> Unit is RoomMemberProfileViewEvents.OnKickActionSuccess -> Unit
is RoomMemberProfileViewEvents.OnSetPowerLevelSuccess -> Unit is RoomMemberProfileViewEvents.OnSetPowerLevelSuccess -> Unit
is RoomMemberProfileViewEvents.OnBanActionSuccess -> Unit is RoomMemberProfileViewEvents.OnBanActionSuccess -> Unit
is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> handleOnIgnoreActionSuccess(it) is RoomMemberProfileViewEvents.OnIgnoreActionSuccess -> Unit
is RoomMemberProfileViewEvents.OnInviteActionSuccess -> Unit is RoomMemberProfileViewEvents.OnInviteActionSuccess -> Unit
} }
} }
setupLongClicks() setupLongClicks()
} }
private fun handleOnIgnoreActionSuccess(action: RoomMemberProfileViewEvents.OnIgnoreActionSuccess) {
if (action.shouldPerformInitialSync) {
// A user has been un-ignored, perform a initial sync
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = true))
}
}
private fun setupLongClicks() { private fun setupLongClicks() {
headerViews.memberProfileNameView.copyOnLongClick() headerViews.memberProfileNameView.copyOnLongClick()
headerViews.memberProfileIdView.copyOnLongClick() headerViews.memberProfileIdView.copyOnLongClick()

View File

@ -25,7 +25,7 @@ sealed class RoomMemberProfileViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : RoomMemberProfileViewEvents() data class Loading(val message: CharSequence? = null) : RoomMemberProfileViewEvents()
data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents() data class Failure(val throwable: Throwable) : RoomMemberProfileViewEvents()
data class OnIgnoreActionSuccess(val shouldPerformInitialSync: Boolean) : RoomMemberProfileViewEvents() object OnIgnoreActionSuccess : RoomMemberProfileViewEvents()
object OnSetPowerLevelSuccess : RoomMemberProfileViewEvents() object OnSetPowerLevelSuccess : RoomMemberProfileViewEvents()
object OnInviteActionSuccess : RoomMemberProfileViewEvents() object OnInviteActionSuccess : RoomMemberProfileViewEvents()
object OnKickActionSuccess : RoomMemberProfileViewEvents() object OnKickActionSuccess : RoomMemberProfileViewEvents()

View File

@ -390,7 +390,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(
} else { } else {
session.ignoreUserIds(listOf(state.userId)) session.ignoreUserIds(listOf(state.userId))
} }
RoomMemberProfileViewEvents.OnIgnoreActionSuccess(isIgnored) RoomMemberProfileViewEvents.OnIgnoreActionSuccess
} catch (failure: Throwable) { } catch (failure: Throwable) {
RoomMemberProfileViewEvents.Failure(failure) RoomMemberProfileViewEvents.Failure(failure)
} }

View File

@ -30,8 +30,6 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import javax.inject.Inject import javax.inject.Inject
@ -62,16 +60,11 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
when (it) { when (it) {
is IgnoredUsersViewEvents.Loading -> showLoading(it.message) is IgnoredUsersViewEvents.Loading -> showLoading(it.message)
is IgnoredUsersViewEvents.Failure -> showFailure(it.throwable) is IgnoredUsersViewEvents.Failure -> showFailure(it.throwable)
IgnoredUsersViewEvents.Success -> handleSuccess() IgnoredUsersViewEvents.Success -> Unit
} }
} }
} }
private fun handleSuccess() {
// A user has been un-ignored, perform a initial sync
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = true))
}
override fun onDestroyView() { override fun onDestroyView() {
ignoredUsersController.callback = null ignoredUsersController.callback = null
views.genericRecyclerView.cleanup() views.genericRecyclerView.cleanup()

View File

@ -176,6 +176,9 @@
<string name="initial_sync_start_importing_account_groups">Initial sync:\nImporting communities</string> <string name="initial_sync_start_importing_account_groups">Initial sync:\nImporting communities</string>
<string name="initial_sync_start_importing_account_data">Initial sync:\nImporting account data</string> <string name="initial_sync_start_importing_account_data">Initial sync:\nImporting account data</string>
<string name="initial_sync_request_title">Initial sync request</string>
<string name="initial_sync_request_content">${app_name} needs to perform a clear cache to be up to date, for the following reason:\n%s\n\nNote that this action will restart the app and it may take some time.</string>
<string name="initial_sync_request_reason_unignored_users">- Some users have been unignored</string>
<string name="event_status_sent_message">Message sent</string> <string name="event_status_sent_message">Message sent</string>
<string name="event_status_sending_message">Sending message…</string> <string name="event_status_sending_message">Sending message…</string>
@ -634,7 +637,7 @@
<string name="room_participants_action_ignore">Ignore</string> <string name="room_participants_action_ignore">Ignore</string>
<string name="room_participants_action_unignore_title">Unignore user</string> <string name="room_participants_action_unignore_title">Unignore user</string>
<string name="room_participants_action_unignore_prompt_msg">Unignoring this user will show all messages from them again.\n\nNote that this action will restart the app and it may take some time.</string> <string name="room_participants_action_unignore_prompt_msg">Unignoring this user will show all messages from them again.</string>
<string name="room_participants_action_unignore">Unignore</string> <string name="room_participants_action_unignore">Unignore</string>
<string name="room_participants_action_cancel_invite_title">Cancel invite</string> <string name="room_participants_action_cancel_invite_title">Cancel invite</string>
@ -1035,7 +1038,7 @@
<string name="settings_fail_to_update_password">Failed to update password</string> <string name="settings_fail_to_update_password">Failed to update password</string>
<string name="settings_fail_to_update_password_invalid_current_password">The password is not valid</string> <string name="settings_fail_to_update_password_invalid_current_password">The password is not valid</string>
<string name="settings_password_updated">Your password has been updated</string> <string name="settings_password_updated">Your password has been updated</string>
<string name="settings_unignore_user">Show all messages from %s?\n\nNote that this action will restart the app and it may take some time.</string> <string name="settings_unignore_user">Show all messages from %s?</string>
<string name="settings_emails_and_phone_numbers_title">Emails and phone numbers</string> <string name="settings_emails_and_phone_numbers_title">Emails and phone numbers</string>
<string name="settings_emails_and_phone_numbers_summary">Manage emails and phone numbers linked to your Matrix account</string> <string name="settings_emails_and_phone_numbers_summary">Manage emails and phone numbers linked to your Matrix account</string>