Merge pull request #668 from vector-im/feature/block_user
Block user and display ignored users list
This commit is contained in:
commit
c5b8c69ae5
@ -2,7 +2,7 @@ Changes in RiotX 0.8.0 (2019-XX-XX)
|
||||
===================================================
|
||||
|
||||
Features ✨:
|
||||
-
|
||||
- Ignore/UnIgnore users, and display list of ignored users (#542, #617)
|
||||
|
||||
Improvements 🙌:
|
||||
- Search reaction by name or keyword in emoji picker
|
||||
|
@ -54,6 +54,10 @@ class RxSession(private val session: Session) {
|
||||
return session.liveUsers().asObservable()
|
||||
}
|
||||
|
||||
fun liveIgnoredUsers(): Observable<List<User>> {
|
||||
return session.liveIgnoredUsers().asObservable()
|
||||
}
|
||||
|
||||
fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
|
||||
return session.livePagedUsers(filter).asObservable()
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.cache
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
|
||||
/**
|
||||
* This interface defines a method to sign out. It's implemented at the session level.
|
||||
* This interface defines a method to clear the cache. It's implemented at the session level.
|
||||
*/
|
||||
interface CacheService {
|
||||
|
||||
|
@ -64,4 +64,19 @@ interface UserService {
|
||||
* @return a Livedata of users
|
||||
*/
|
||||
fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
|
||||
|
||||
/**
|
||||
* Get list of ignored users
|
||||
*/
|
||||
fun liveIgnoredUsers(): LiveData<List<User>>
|
||||
|
||||
/**
|
||||
* Ignore users
|
||||
*/
|
||||
fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Un-ignore some users
|
||||
*/
|
||||
fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
|
||||
}
|
||||
|
@ -21,4 +21,6 @@ import java.lang.reflect.ParameterizedType
|
||||
|
||||
typealias JsonDict = Map<String, @JvmSuppressWildcards Any>
|
||||
|
||||
val emptyJsonDict = emptyMap<String, Any>()
|
||||
|
||||
internal val JSON_DICT_PARAMETERIZED_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.database.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
|
||||
internal open class IgnoredUserEntity(var userId: String = "") : RealmObject() {
|
||||
|
||||
companion object
|
||||
}
|
@ -35,6 +35,7 @@ import io.realm.annotations.RealmModule
|
||||
RoomTagEntity::class,
|
||||
SyncEntity::class,
|
||||
UserEntity::class,
|
||||
IgnoredUserEntity::class,
|
||||
EventAnnotationsSummaryEntity::class,
|
||||
ReactionAggregatedSummaryEntity::class,
|
||||
EditAggregatedSummaryEntity::class,
|
||||
|
@ -20,10 +20,11 @@ import com.squareup.moshi.Moshi
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory
|
||||
import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
|
||||
|
||||
object MoshiProvider {
|
||||
|
||||
@ -31,6 +32,7 @@ object MoshiProvider {
|
||||
.add(UriMoshiAdapter())
|
||||
.add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
|
||||
.registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
|
||||
.registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST)
|
||||
.registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
|
||||
)
|
||||
.add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
|
||||
|
@ -27,10 +27,9 @@ import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.session.pushers.SavePushRulesTask
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMembers
|
||||
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.*
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
@ -44,6 +43,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
||||
private val directChatsHelper: DirectChatsHelper,
|
||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||
private val savePushRulesTask: SavePushRulesTask,
|
||||
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
|
||||
suspend fun handle(accountData: UserAccountDataSync?, invites: Map<String, InvitedRoomSync>?) {
|
||||
@ -51,9 +51,18 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
||||
when (it) {
|
||||
is UserAccountDataDirectMessages -> handleDirectChatRooms(it)
|
||||
is UserAccountDataPushRules -> handlePushRules(it)
|
||||
else -> return@forEach
|
||||
is UserAccountDataIgnoredUsers -> handleIgnoredUsers(it)
|
||||
is UserAccountDataFallback -> Timber.d("Receive account data of unhandled type ${it.type}")
|
||||
else -> error("Missing code here!")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Store all account data, app can be interested of it
|
||||
// accountData?.list?.forEach {
|
||||
// it.toString()
|
||||
// MoshiProvider.providesMoshi()
|
||||
// }
|
||||
|
||||
monarchy.doWithRealm { realm ->
|
||||
synchronizeWithServerIfNeeded(realm, invites)
|
||||
}
|
||||
@ -114,4 +123,11 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
|
||||
updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) {
|
||||
saveIgnoredUsersTask
|
||||
.configureWith(SaveIgnoredUsersTask.Params(userAccountDataIgnoredUsers.content.ignoredUsers.keys.toList()))
|
||||
.executeBy(taskExecutor)
|
||||
// TODO If not initial sync, we should execute a init sync
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.sync.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
|
||||
|
||||
// SyncResponse represents the request response for server sync v2.
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
import im.vector.matrix.android.api.util.emptyJsonDict
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class IgnoredUsersContent(
|
||||
/**
|
||||
* Required. The map of users to ignore. UserId -> empty object for future enhancement
|
||||
*/
|
||||
@Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict>
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun createWithUserIds(userIds: List<String>): IgnoredUsersContent {
|
||||
return IgnoredUsersContent(
|
||||
ignoredUsers = userIds.associateWith { emptyJsonDict }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -14,9 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
internal interface UserAccountData {
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
internal abstract class UserAccountData {
|
||||
|
||||
@Json(name = "type") abstract val type: String
|
||||
|
||||
companion object {
|
||||
const val TYPE_IGNORED_USER_LIST = "m.ignored_user_list"
|
@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataDirectMessages(
|
||||
@Json(name = "type") override val type: String = TYPE_DIRECT_MESSAGES,
|
||||
@Json(name = "content") val content: Map<String, List<String>>
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataFallback(
|
||||
@Json(name = "type") override val type: String,
|
||||
@Json(name = "content") val content: Map<String, Any>
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataIgnoredUsers(
|
||||
@Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST,
|
||||
@Json(name = "content") val content: IgnoredUsersContent
|
||||
) : UserAccountData()
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
@ -22,5 +22,6 @@ import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class UserAccountDataPushRules(
|
||||
@Json(name = "type") override val type: String = TYPE_PUSH_RULES,
|
||||
@Json(name = "content") val content: GetPushRulesResponse
|
||||
) : UserAccountData
|
||||
) : UserAccountData()
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.matrix.android.internal.session.sync.model
|
||||
package im.vector.matrix.android.internal.session.sync.model.accountdata
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
@ -29,9 +29,12 @@ import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
@ -40,8 +43,8 @@ import javax.inject.Inject
|
||||
|
||||
internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy,
|
||||
private val searchUserTask: SearchUserTask,
|
||||
private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask,
|
||||
private val taskExecutor: TaskExecutor) : UserService {
|
||||
|
||||
private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
|
||||
monarchy.createDataSourceFactory { realm ->
|
||||
realm.where(UserEntity::class.java)
|
||||
@ -62,7 +65,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
||||
|
||||
override fun getUser(userId: String): User? {
|
||||
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
||||
?: return null
|
||||
?: return null
|
||||
|
||||
return userEntity.asDomain()
|
||||
}
|
||||
@ -117,4 +120,33 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun liveIgnoredUsers(): LiveData<List<User>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ realm ->
|
||||
realm.where(IgnoredUserEntity::class.java)
|
||||
.isNotEmpty(IgnoredUserEntityFields.USER_ID)
|
||||
.sort(IgnoredUserEntityFields.USER_ID)
|
||||
},
|
||||
{ getUser(it.userId) ?: User(userId = it.userId) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val params = UpdateIgnoredUserIdsTask.Params(userIdsToIgnore = userIds.toList())
|
||||
return updateIgnoredUserIdsTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val params = UpdateIgnoredUserIdsTask.Params(userIdsToUnIgnore = userIds.toList())
|
||||
return updateIgnoredUserIdsTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.session.user.UserService
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DefaultSaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
|
||||
import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
|
||||
import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask
|
||||
import im.vector.matrix.android.internal.session.user.model.SearchUserTask
|
||||
import retrofit2.Retrofit
|
||||
@ -43,4 +47,10 @@ internal abstract class UserModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindSearchUserTask(searchUserTask: DefaultSearchUserTask): SearchUserTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSaveIgnoredUsersTask(task: DefaultSaveIgnoredUsersTask): SaveIgnoredUsersTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.user.accountdata
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.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(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 } }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2019 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.matrix.android.internal.session.user.accountdata
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.IgnoredUsersContent
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Params, Unit> {
|
||||
|
||||
data class Params(
|
||||
val userIdsToIgnore: List<String> = emptyList(),
|
||||
val userIdsToUnIgnore: List<String> = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI,
|
||||
private val monarchy: Monarchy,
|
||||
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
|
||||
@UserId private val userId: String) : UpdateIgnoredUserIdsTask {
|
||||
|
||||
override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
|
||||
// Get current list
|
||||
val ignoredUserIds = monarchy.fetchAllMappedSync(
|
||||
{ realm -> realm.where(IgnoredUserEntity::class.java) },
|
||||
{ it.userId }
|
||||
).toMutableSet()
|
||||
|
||||
val original = ignoredUserIds.toList()
|
||||
|
||||
ignoredUserIds.removeAll { it in params.userIdsToUnIgnore }
|
||||
ignoredUserIds.addAll(params.userIdsToIgnore)
|
||||
|
||||
if (original == ignoredUserIds) {
|
||||
// No change
|
||||
return
|
||||
}
|
||||
|
||||
val list = ignoredUserIds.toList()
|
||||
val body = IgnoredUsersContent.createWithUserIds(list)
|
||||
|
||||
executeRequest<Unit> {
|
||||
apiCall = accountDataApi.setAccountData(userId, UserAccountData.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))
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata
|
||||
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.sync.model.UserAccountData
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -29,6 +29,7 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
|
||||
fun getData(): Any
|
||||
}
|
||||
|
||||
// TODO Use [UserAccountDataDirectMessages] class?
|
||||
data class DirectChatParams(override val type: String = UserAccountData.TYPE_DIRECT_MESSAGES,
|
||||
private val directMessages: Map<String, List<String>>
|
||||
) : Params {
|
||||
|
@ -16,13 +16,21 @@
|
||||
|
||||
package im.vector.riotx.core.platform
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.airbnb.mvrx.*
|
||||
import im.vector.riotx.core.utils.LiveEvent
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
abstract class VectorViewModel<S : MvRxState>(initialState: S)
|
||||
: BaseMvRxViewModel<S>(initialState, false) {
|
||||
|
||||
// Generic handling of any request error
|
||||
protected val _requestErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
|
||||
val requestErrorLiveData: LiveData<LiveEvent<Throwable>>
|
||||
get() = _requestErrorLiveData
|
||||
|
||||
/**
|
||||
* This method does the same thing as the execute function, but it doesn't subscribe to the stream
|
||||
* so you can use this in a switchMap or a flatMap
|
||||
|
@ -50,7 +50,14 @@ sealed class RoomDetailActions {
|
||||
data class ResendMessage(val eventId: String) : RoomDetailActions()
|
||||
data class RemoveFailedEcho(val eventId: String) : RoomDetailActions()
|
||||
|
||||
data class ReportContent(val eventId: String, val reason: String, val spam: Boolean = false, val inappropriate: Boolean = false) : RoomDetailActions()
|
||||
data class ReportContent(
|
||||
val eventId: String,
|
||||
val senderId: String?,
|
||||
val reason: String,
|
||||
val spam: Boolean = false,
|
||||
val inappropriate: Boolean = false) : RoomDetailActions()
|
||||
|
||||
data class IgnoreUser(val userId: String?) : RoomDetailActions()
|
||||
|
||||
object ClearSendQueue : RoomDetailActions()
|
||||
object ResendAll : RoomDetailActions()
|
||||
|
@ -757,7 +757,7 @@ class RoomDetailFragment @Inject constructor(
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.report_content_custom_submit) { _, _ ->
|
||||
val reason = input.text.toString()
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, reason))
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, action.senderId, reason))
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
@ -781,7 +781,9 @@ class RoomDetailFragment @Inject constructor(
|
||||
.setTitle(R.string.content_reported_as_spam_title)
|
||||
.setMessage(R.string.content_reported_as_spam_content)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
|
||||
.setNegativeButton(R.string.block_user) { _, _ ->
|
||||
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
|
||||
}
|
||||
@ -790,7 +792,9 @@ class RoomDetailFragment @Inject constructor(
|
||||
.setTitle(R.string.content_reported_as_inappropriate_title)
|
||||
.setMessage(R.string.content_reported_as_inappropriate_content)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
|
||||
.setNegativeButton(R.string.block_user) { _, _ ->
|
||||
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
|
||||
}
|
||||
@ -799,7 +803,9 @@ class RoomDetailFragment @Inject constructor(
|
||||
.setTitle(R.string.content_reported_title)
|
||||
.setMessage(R.string.content_reported_content)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
|
||||
.setNegativeButton(R.string.block_user) { _, _ ->
|
||||
roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_NEGATIVE)
|
||||
}
|
||||
@ -1124,10 +1130,12 @@ class RoomDetailFragment @Inject constructor(
|
||||
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId))
|
||||
}
|
||||
is SimpleAction.ReportContentSpam -> {
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is spam", spam = true))
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(
|
||||
action.eventId, action.senderId, "This message is spam", spam = true))
|
||||
}
|
||||
is SimpleAction.ReportContentInappropriate -> {
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is inappropriate", inappropriate = true))
|
||||
roomDetailViewModel.process(RoomDetailActions.ReportContent(
|
||||
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
|
||||
}
|
||||
is SimpleAction.ReportContentCustom -> {
|
||||
promptReasonToReportContent(action)
|
||||
|
@ -157,6 +157,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
is RoomDetailActions.SetReadMarkerAction -> handleSetReadMarkerAction(action)
|
||||
is RoomDetailActions.MarkAllAsRead -> handleMarkAllAsRead()
|
||||
is RoomDetailActions.ReportContent -> handleReportContent(action)
|
||||
is RoomDetailActions.IgnoreUser -> handleIgnoreUser(action)
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,6 +711,22 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleIgnoreUser(action: RoomDetailActions.IgnoreUser) {
|
||||
if (action.userId.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
session.ignoreUserIds(listOf(action.userId), object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
_requestLiveData.postValue(LiveEvent(Success(action)))
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
_requestLiveData.postValue(LiveEvent(Fail(failure)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun observeSyncState() {
|
||||
session.rx()
|
||||
.liveSyncState()
|
||||
|
@ -102,9 +102,9 @@ class MessageActionsEpoxyController @Inject constructor(private val stringProvid
|
||||
if (action is SimpleAction.ReportContent && state.expendedReportContentMenu) {
|
||||
// Special case for report content menu: add the submenu
|
||||
listOf(
|
||||
SimpleAction.ReportContentSpam(action.eventId),
|
||||
SimpleAction.ReportContentInappropriate(action.eventId),
|
||||
SimpleAction.ReportContentCustom(action.eventId)
|
||||
SimpleAction.ReportContentSpam(action.eventId, action.senderId),
|
||||
SimpleAction.ReportContentInappropriate(action.eventId, action.senderId),
|
||||
SimpleAction.ReportContentCustom(action.eventId, action.senderId)
|
||||
).forEachIndexed { indexReport, actionReport ->
|
||||
bottomSheetItemAction {
|
||||
id("actionReport_$indexReport")
|
||||
|
@ -263,7 +263,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||
|
||||
if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
|
||||
// not sent by me
|
||||
add(SimpleAction.ReportContent(eventId))
|
||||
add(SimpleAction.ReportContent(eventId, event.root.senderId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,25 +22,63 @@ import im.vector.riotx.R
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||
|
||||
sealed class SimpleAction(@StringRes val titleRes: Int, @DrawableRes val iconResId: Int) {
|
||||
data class AddReaction(val eventId: String) : SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction)
|
||||
data class Copy(val content: String) : SimpleAction(R.string.copy, R.drawable.ic_copy)
|
||||
data class Edit(val eventId: String) : SimpleAction(R.string.edit, R.drawable.ic_edit)
|
||||
data class Quote(val eventId: String) : SimpleAction(R.string.quote, R.drawable.ic_quote)
|
||||
data class Reply(val eventId: String) : SimpleAction(R.string.reply, R.drawable.ic_reply)
|
||||
data class Share(val imageUrl: String) : SimpleAction(R.string.share, R.drawable.ic_share)
|
||||
data class Resend(val eventId: String) : SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw)
|
||||
data class Remove(val eventId: String) : SimpleAction(R.string.remove, R.drawable.ic_trash)
|
||||
data class Delete(val eventId: String) : SimpleAction(R.string.delete, R.drawable.ic_delete)
|
||||
data class Cancel(val eventId: String) : SimpleAction(R.string.cancel, R.drawable.ic_close_round)
|
||||
data class ViewSource(val content: String) : SimpleAction(R.string.view_source, R.drawable.ic_view_source)
|
||||
data class ViewDecryptedSource(val content: String) : SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source)
|
||||
data class CopyPermalink(val eventId: String) : SimpleAction(R.string.permalink, R.drawable.ic_permalink)
|
||||
data class ReportContent(val eventId: String) : SimpleAction(R.string.report_content, R.drawable.ic_flag)
|
||||
data class ReportContentSpam(val eventId: String) : SimpleAction(R.string.report_content_spam, R.drawable.ic_report_spam)
|
||||
data class ReportContentInappropriate(val eventId: String) : SimpleAction(R.string.report_content_inappropriate, R.drawable.ic_report_inappropriate)
|
||||
data class ReportContentCustom(val eventId: String) : SimpleAction(R.string.report_content_custom, R.drawable.ic_report_custom)
|
||||
data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : SimpleAction(0, 0)
|
||||
data class ViewReactions(val messageInformationData: MessageInformationData) : SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
|
||||
data class AddReaction(val eventId: String) :
|
||||
SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction)
|
||||
|
||||
data class Copy(val content: String) :
|
||||
SimpleAction(R.string.copy, R.drawable.ic_copy)
|
||||
|
||||
data class Edit(val eventId: String) :
|
||||
SimpleAction(R.string.edit, R.drawable.ic_edit)
|
||||
|
||||
data class Quote(val eventId: String) :
|
||||
SimpleAction(R.string.quote, R.drawable.ic_quote)
|
||||
|
||||
data class Reply(val eventId: String) :
|
||||
SimpleAction(R.string.reply, R.drawable.ic_reply)
|
||||
|
||||
data class Share(val imageUrl: String) :
|
||||
SimpleAction(R.string.share, R.drawable.ic_share)
|
||||
|
||||
data class Resend(val eventId: String) :
|
||||
SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw)
|
||||
|
||||
data class Remove(val eventId: String) :
|
||||
SimpleAction(R.string.remove, R.drawable.ic_trash)
|
||||
|
||||
data class Delete(val eventId: String) :
|
||||
SimpleAction(R.string.delete, R.drawable.ic_delete)
|
||||
|
||||
data class Cancel(val eventId: String) :
|
||||
SimpleAction(R.string.cancel, R.drawable.ic_close_round)
|
||||
|
||||
data class ViewSource(val content: String) :
|
||||
SimpleAction(R.string.view_source, R.drawable.ic_view_source)
|
||||
|
||||
data class ViewDecryptedSource(val content: String) :
|
||||
SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source)
|
||||
|
||||
data class CopyPermalink(val eventId: String) :
|
||||
SimpleAction(R.string.permalink, R.drawable.ic_permalink)
|
||||
|
||||
data class ReportContent(val eventId: String, val senderId: String?) :
|
||||
SimpleAction(R.string.report_content, R.drawable.ic_flag)
|
||||
|
||||
data class ReportContentSpam(val eventId: String, val senderId: String?) :
|
||||
SimpleAction(R.string.report_content_spam, R.drawable.ic_report_spam)
|
||||
|
||||
data class ReportContentInappropriate(val eventId: String, val senderId: String?) :
|
||||
SimpleAction(R.string.report_content_inappropriate, R.drawable.ic_report_inappropriate)
|
||||
|
||||
data class ReportContentCustom(val eventId: String, val senderId: String?) :
|
||||
SimpleAction(R.string.report_content_custom, R.drawable.ic_report_custom)
|
||||
|
||||
data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) :
|
||||
SimpleAction(0, 0)
|
||||
|
||||
data class ViewReactions(val messageInformationData: MessageInformationData) :
|
||||
SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
|
||||
|
||||
data class ViewEditHistory(val messageInformationData: MessageInformationData) :
|
||||
SimpleAction(R.string.message_view_edit_history, R.drawable.ic_view_edit_history)
|
||||
}
|
||||
|
@ -55,8 +55,6 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||
const val SETTINGS_CONTACT_PREFERENCE_KEYS = "SETTINGS_CONTACT_PREFERENCE_KEYS"
|
||||
const val SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY = "SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY"
|
||||
const val SETTINGS_NOTIFICATIONS_TARGET_DIVIDER_PREFERENCE_KEY = "SETTINGS_NOTIFICATIONS_TARGET_DIVIDER_PREFERENCE_KEY"
|
||||
const val SETTINGS_IGNORED_USERS_PREFERENCE_KEY = "SETTINGS_IGNORED_USERS_PREFERENCE_KEY"
|
||||
const val SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY = "SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY"
|
||||
const val SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY"
|
||||
const val SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY"
|
||||
const val SETTINGS_LABS_PREFERENCE_KEY = "SETTINGS_LABS_PREFERENCE_KEY"
|
||||
@ -544,7 +542,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||
MEDIA_SAVING_1_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60
|
||||
MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60
|
||||
MEDIA_SAVING_FOREVER -> 0
|
||||
else -> 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,7 +557,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||
MEDIA_SAVING_1_WEEK -> context.getString(R.string.media_saving_period_1_week)
|
||||
MEDIA_SAVING_1_MONTH -> context.getString(R.string.media_saving_period_1_month)
|
||||
MEDIA_SAVING_FOREVER -> context.getString(R.string.media_saving_period_forever)
|
||||
else -> "?"
|
||||
else -> "?"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.settings
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.preference.VectorPreference
|
||||
import java.util.ArrayList
|
||||
import kotlin.Comparator
|
||||
|
||||
class VectorSettingsIgnoredUsersFragment : VectorSettingsBaseFragment() {
|
||||
|
||||
override var titleRes = R.string.settings_ignored_users
|
||||
override val preferenceXmlRes = R.xml.vector_settings_ignored_users
|
||||
|
||||
// displayed the ignored users list
|
||||
private val mIgnoredUserSettingsCategoryDivider by lazy {
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY)!!
|
||||
}
|
||||
private val mIgnoredUserSettingsCategory by lazy {
|
||||
findPreference<PreferenceCategory>(VectorPreferences.SETTINGS_IGNORED_USERS_PREFERENCE_KEY)!!
|
||||
}
|
||||
|
||||
override fun bindPref() {
|
||||
// Ignore users
|
||||
refreshIgnoredUsersList()
|
||||
}
|
||||
|
||||
// ==============================================================================================================
|
||||
// ignored users list management
|
||||
// ==============================================================================================================
|
||||
|
||||
/**
|
||||
* Refresh the ignored users list
|
||||
*/
|
||||
private fun refreshIgnoredUsersList() {
|
||||
val ignoredUsersList = mutableListOf<String>() // TODO session.dataHandler.ignoredUserIds
|
||||
|
||||
ignoredUsersList.sortWith(Comparator { u1, u2 ->
|
||||
u1.toLowerCase(VectorLocale.applicationLocale).compareTo(u2.toLowerCase(VectorLocale.applicationLocale))
|
||||
})
|
||||
|
||||
val preferenceScreen = preferenceScreen
|
||||
|
||||
preferenceScreen.removePreference(mIgnoredUserSettingsCategory)
|
||||
preferenceScreen.removePreference(mIgnoredUserSettingsCategoryDivider)
|
||||
mIgnoredUserSettingsCategory.removeAll()
|
||||
|
||||
if (ignoredUsersList.size > 0) {
|
||||
preferenceScreen.addPreference(mIgnoredUserSettingsCategoryDivider)
|
||||
preferenceScreen.addPreference(mIgnoredUserSettingsCategory)
|
||||
|
||||
for (userId in ignoredUsersList) {
|
||||
val preference = Preference(activity)
|
||||
|
||||
preference.title = userId
|
||||
preference.key = IGNORED_USER_KEY_BASE + userId
|
||||
|
||||
preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
activity?.let {
|
||||
AlertDialog.Builder(it)
|
||||
.setMessage(getString(R.string.settings_unignore_user, userId))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
displayLoadingView()
|
||||
|
||||
val idsList = ArrayList<String>()
|
||||
idsList.add(userId)
|
||||
|
||||
notImplemented()
|
||||
/* TODO
|
||||
session.unIgnoreUsers(idsList, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(info: Void?) {
|
||||
onCommonDone(null)
|
||||
}
|
||||
|
||||
override fun onNetworkError(e: Exception) {
|
||||
onCommonDone(e.localizedMessage)
|
||||
}
|
||||
|
||||
override fun onMatrixError(e: MatrixError) {
|
||||
onCommonDone(e.localizedMessage)
|
||||
}
|
||||
|
||||
override fun onUnexpectedError(e: Exception) {
|
||||
onCommonDone(e.localizedMessage)
|
||||
}
|
||||
})
|
||||
*/
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
mIgnoredUserSettingsCategory.addPreference(preference)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val IGNORED_USER_KEY_BASE = "IGNORED_USER_KEY_BASE"
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.settings.ignored
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.noResultItem
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class IgnoredUsersController @Inject constructor(private val stringProvider: StringProvider,
|
||||
private val avatarRenderer: AvatarRenderer) : EpoxyController() {
|
||||
|
||||
var callback: Callback? = null
|
||||
private var viewState: IgnoredUsersViewState? = null
|
||||
|
||||
init {
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
fun update(viewState: IgnoredUsersViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val nonNullViewState = viewState ?: return
|
||||
buildIgnoredUserModels(nonNullViewState.ignoredUsers)
|
||||
}
|
||||
|
||||
private fun buildIgnoredUserModels(userIds: List<User>) {
|
||||
if (userIds.isEmpty()) {
|
||||
noResultItem {
|
||||
id("empty")
|
||||
text(stringProvider.getString(R.string.no_ignored_users))
|
||||
}
|
||||
} else {
|
||||
userIds.forEach { userId ->
|
||||
userItem {
|
||||
id(userId.userId)
|
||||
avatarRenderer(avatarRenderer)
|
||||
user(userId)
|
||||
itemClickAction { callback?.onUserIdClicked(userId.userId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onUserIdClicked(userId: String)
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.settings.ignored
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotx.core.extensions.postLiveEvent
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
data class IgnoredUsersViewState(
|
||||
val ignoredUsers: List<User> = emptyList(),
|
||||
val unIgnoreRequest: Async<Unit> = Uninitialized
|
||||
) : MvRxState
|
||||
|
||||
sealed class IgnoredUsersAction {
|
||||
data class UnIgnore(val userId: String) : IgnoredUsersAction()
|
||||
}
|
||||
|
||||
class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
|
||||
private val session: Session) : VectorViewModel<IgnoredUsersViewState>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<IgnoredUsersViewModel, IgnoredUsersViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: IgnoredUsersViewState): IgnoredUsersViewModel? {
|
||||
val ignoredUsersFragment: VectorSettingsIgnoredUsersFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return ignoredUsersFragment.ignoredUsersViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
observeIgnoredUsers()
|
||||
}
|
||||
|
||||
private fun observeIgnoredUsers() {
|
||||
session.rx()
|
||||
.liveIgnoredUsers()
|
||||
.execute { async ->
|
||||
copy(
|
||||
ignoredUsers = async.invoke().orEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun handle(action: IgnoredUsersAction) {
|
||||
when (action) {
|
||||
is IgnoredUsersAction.UnIgnore -> handleUnIgnore(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnIgnore(action: IgnoredUsersAction.UnIgnore) {
|
||||
setState {
|
||||
copy(
|
||||
unIgnoreRequest = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
session.unIgnoreUserIds(listOf(action.userId), object : MatrixCallback<Unit> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
unIgnoreRequest = Fail(failure)
|
||||
)
|
||||
}
|
||||
|
||||
_requestErrorLiveData.postLiveEvent(failure)
|
||||
}
|
||||
|
||||
override fun onSuccess(data: Unit) {
|
||||
setState {
|
||||
copy(
|
||||
unIgnoreRequest = Success(data)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.settings.ignored
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
|
||||
/**
|
||||
* A list item for User.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_user)
|
||||
abstract class UserItem : VectorEpoxyModel<UserItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var user: User
|
||||
|
||||
@EpoxyAttribute
|
||||
var itemClickAction: (() -> Unit)? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.root.setOnClickListener { itemClickAction?.invoke() }
|
||||
|
||||
avatarRenderer.render(user, holder.avatarImage)
|
||||
holder.userIdText.setTextOrHide(user.userId)
|
||||
holder.displayNameText.setTextOrHide(user.displayName)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val root by bind<View>(R.id.itemUserRoot)
|
||||
val avatarImage by bind<ImageView>(R.id.itemUserAvatar)
|
||||
val userIdText by bind<TextView>(R.id.itemUserId)
|
||||
val displayNameText by bind<TextView>(R.id.itemUserName)
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.settings.ignored
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
||||
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorSettingsIgnoredUsersFragment @Inject constructor(
|
||||
val ignoredUsersViewModelFactory: IgnoredUsersViewModel.Factory,
|
||||
private val ignoredUsersController: IgnoredUsersController,
|
||||
private val errorFormatter: ErrorFormatter
|
||||
) : VectorBaseFragment(), IgnoredUsersController.Callback {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler_epoxy
|
||||
|
||||
private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
waiting_view_status_text.setText(R.string.please_wait)
|
||||
waiting_view_status_text.isVisible = true
|
||||
ignoredUsersController.callback = this
|
||||
epoxyRecyclerView.setController(ignoredUsersController)
|
||||
ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) {
|
||||
displayErrorDialog(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_ignored_users)
|
||||
}
|
||||
|
||||
override fun onUserIdClicked(userId: String) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setMessage(getString(R.string.settings_unignore_user, userId))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
ignoredUsersViewModel.handle(IgnoredUsersAction.UnIgnore(userId))
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun displayErrorDialog(throwable: Throwable) {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(errorFormatter.toHumanReadable(throwable))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
// ==============================================================================================================
|
||||
// ignored users list management
|
||||
// ==============================================================================================================
|
||||
|
||||
override fun invalidate() = withState(ignoredUsersViewModel) { state ->
|
||||
ignoredUsersController.update(state)
|
||||
|
||||
handleUnIgnoreRequestStatus(state.unIgnoreRequest)
|
||||
}
|
||||
|
||||
private fun handleUnIgnoreRequestStatus(unIgnoreRequest: Async<Unit>) {
|
||||
when (unIgnoreRequest) {
|
||||
is Loading -> waiting_view.isVisible = true
|
||||
else -> waiting_view.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/epoxyRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:itemSpacing="1dp"
|
||||
tools:listitem="@layout/item_pushgateway" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/epoxyRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:itemSpacing="1dp"
|
||||
tools:listitem="@layout/item_pushgateway" />
|
||||
|
||||
<include layout="@layout/merge_overlay_waiting_view" />
|
||||
|
||||
</FrameLayout>
|
||||
|
58
vector/src/main/res/layout/item_user.xml
Normal file
58
vector/src/main/res/layout/item_user.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemUserRoot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?riotx_background"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemUserAvatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemUserId"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemUserName"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/itemUserAvatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@sample/matrix.json/data/mxid" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemUserName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/itemUserAvatar"
|
||||
app:layout_constraintTop_toBottomOf="@+id/itemUserId"
|
||||
tools:text="@sample/matrix.json/data/displayName"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -3,8 +3,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/waiting_view"
|
||||
|
@ -4,4 +4,6 @@
|
||||
<!-- Strings not defined in Riot -->
|
||||
<string name="notice_member_no_changes">"%1$s made no changes"</string>
|
||||
|
||||
<string name="no_ignored_users">You are not ignoring any users</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreferenceCategory
|
||||
android:key="SETTINGS_IGNORED_USERS_PREFERENCE_KEY"
|
||||
android:title="@string/settings_ignored_users" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreferenceDivider android:key="SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY" />
|
||||
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
@ -37,10 +37,9 @@
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:layout_width="match_parent"
|
||||
android:enabled="@bool/false_not_implemented"
|
||||
android:icon="@drawable/ic_settings_root_ignored_users"
|
||||
android:title="@string/settings_ignored_users"
|
||||
app:fragment="im.vector.riotx.features.settings.VectorSettingsIgnoredUsersFragment" />
|
||||
app:fragment="im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:layout_width="match_parent"
|
||||
|
Loading…
x
Reference in New Issue
Block a user