Display ignored users list

This commit is contained in:
Benoit Marty 2019-11-05 15:12:23 +01:00
parent fbae3d27c2
commit 9c952b6bc8
18 changed files with 394 additions and 177 deletions

View file

@ -125,7 +125,9 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
} }
private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) { private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) {
saveIgnoredUsersTask.configureWith(userAccountDataIgnoredUsers).executeBy(taskExecutor) saveIgnoredUsersTask
.configureWith(SaveIgnoredUsersTask.Params(userAccountDataIgnoredUsers.content.ignoredUsers.keys.toList()))
.executeBy(taskExecutor)
// TODO If not initial sync, we should execute a init sync // TODO If not initial sync, we should execute a init sync
} }
} }

View file

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.emptyJsonDict
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class IgnoredUsersContent( internal data class IgnoredUsersContent(
@ -26,4 +27,13 @@ internal data class IgnoredUsersContent(
* Required. The map of users to ignore. UserId -> empty object for future enhancement * Required. The map of users to ignore. UserId -> empty object for future enhancement
*/ */
@Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict> @Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict>
) ) {
companion object {
fun createWithUserIds(userIds: List<String>): IgnoredUsersContent {
return IgnoredUsersContent(
ignoredUsers = userIds.associateWith { emptyJsonDict }
)
}
}
}

View file

@ -18,24 +18,9 @@ package im.vector.matrix.android.internal.session.sync.model.accountdata
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.util.emptyJsonDict
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class UserAccountDataIgnoredUsers( internal data class UserAccountDataIgnoredUsers(
@Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST, @Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST,
@Json(name = "content") val content: IgnoredUsersContent @Json(name = "content") val content: IgnoredUsersContent
) : UserAccountData() { ) : UserAccountData()
companion object {
fun createWithUserIds(userIds: List<String>): UserAccountDataIgnoredUsers {
return UserAccountDataIgnoredUsers(
content = IgnoredUsersContent(
ignoredUsers = userIds.associateWith { emptyJsonDict }
)
)
}
}
}

View file

@ -16,9 +16,7 @@
package im.vector.matrix.android.internal.session.user.accountdata package im.vector.matrix.android.internal.session.user.accountdata
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction import im.vector.matrix.android.internal.util.awaitTransaction
import javax.inject.Inject import javax.inject.Inject
@ -26,20 +24,23 @@ import javax.inject.Inject
/** /**
* Save the ignored users list in DB * Save the ignored users list in DB
*/ */
internal interface SaveIgnoredUsersTask : Task<UserAccountDataIgnoredUsers, Unit> { internal interface SaveIgnoredUsersTask : Task<SaveIgnoredUsersTask.Params, Unit> {
data class Params(val pa: GetPushRulesResponse) data class Params(
val userIds: List<String>
)
} }
internal class DefaultSaveIgnoredUsersTask @Inject constructor(private val monarchy: Monarchy) : SaveIgnoredUsersTask { internal class DefaultSaveIgnoredUsersTask @Inject constructor(private val monarchy: Monarchy) : SaveIgnoredUsersTask {
override suspend fun execute(params: UserAccountDataIgnoredUsers) { override suspend fun execute(params: SaveIgnoredUsersTask.Params) {
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->
// clear current ignored users // clear current ignored users
realm.where(IgnoredUserEntity::class.java) realm.where(IgnoredUserEntity::class.java)
.findAll() .findAll()
.deleteAllFromRealm() .deleteAllFromRealm()
params.content.ignoredUsers.keys.forEach { realm.createObject(IgnoredUserEntity::class.java, it) } // And save the new received list
params.userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
} }
} }
} }

View file

@ -20,8 +20,8 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.database.model.IgnoredUserEntity import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest 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.session.sync.model.accountdata.UserAccountData
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject import javax.inject.Inject
@ -36,6 +36,7 @@ internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Para
internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI, internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
@UserId private val userId: String) : UpdateIgnoredUserIdsTask { @UserId private val userId: String) : UpdateIgnoredUserIdsTask {
override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) { override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
@ -43,22 +44,26 @@ internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val a
val ignoredUserIds = monarchy.fetchAllMappedSync( val ignoredUserIds = monarchy.fetchAllMappedSync(
{ realm -> realm.where(IgnoredUserEntity::class.java) }, { realm -> realm.where(IgnoredUserEntity::class.java) },
{ it.userId } { it.userId }
) ).toMutableSet()
val original = ignoredUserIds.toList() val original = ignoredUserIds.toList()
ignoredUserIds -= params.userIdsToUnIgnore ignoredUserIds.removeAll { it in params.userIdsToUnIgnore }
ignoredUserIds += params.userIdsToIgnore ignoredUserIds.addAll(params.userIdsToIgnore)
if (original == ignoredUserIds) { if (original == ignoredUserIds) {
// No change // No change
return return
} }
val body = UserAccountDataIgnoredUsers.createWithUserIds(ignoredUserIds) val list = ignoredUserIds.toList()
val body = IgnoredUsersContent.createWithUserIds(list)
return executeRequest { executeRequest<Unit> {
apiCall = accountDataApi.setAccountData(userId, UserAccountData.TYPE_IGNORED_USER_LIST, body) 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))
} }
} }

View file

@ -16,13 +16,22 @@
package im.vector.riotx.core.platform package im.vector.riotx.core.platform
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.* import com.airbnb.mvrx.*
import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
abstract class VectorViewModel<S : MvRxState>(initialState: S) abstract class VectorViewModel<S : MvRxState>(initialState: S)
: BaseMvRxViewModel<S>(initialState, false) { : 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 * 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 * so you can use this in a switchMap or a flatMap

View file

@ -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_CONTACT_PREFERENCE_KEYS = "SETTINGS_CONTACT_PREFERENCE_KEYS"
const val SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY = "SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY" 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_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_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY"
const val SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_DIVIDER_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" 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_WEEK -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60
MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60 MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60
MEDIA_SAVING_FOREVER -> 0 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_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_1_MONTH -> context.getString(R.string.media_saving_period_1_month)
MEDIA_SAVING_FOREVER -> context.getString(R.string.media_saving_period_forever) MEDIA_SAVING_FOREVER -> context.getString(R.string.media_saving_period_forever)
else -> "?" else -> "?"
} }
} }

View file

@ -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"
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
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
/**
* A list item for ignored user.
*/
@EpoxyModelClass(layout = R.layout.item_ignored_user)
abstract class IgnoredUserItem : VectorEpoxyModel<IgnoredUserItem.Holder>() {
@EpoxyAttribute
var userId: String? = null
@EpoxyAttribute
var itemClickAction: (() -> Unit)? = null
override fun bind(holder: Holder) {
holder.userIdText.setTextOrHide(userId)
holder.userIdText.setOnClickListener { itemClickAction?.invoke() }
}
class Holder : VectorEpoxyHolder() {
val userIdText by bind<TextView>(R.id.itemIgnoredUserId)
}
}

View file

@ -0,0 +1,64 @@
/*
* 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.riotx.R
import im.vector.riotx.core.epoxy.noResultItem
import im.vector.riotx.core.resources.StringProvider
import javax.inject.Inject
class IgnoredUsersController @Inject constructor(private val stringProvider: StringProvider) : 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.ignoredUserIds)
}
private fun buildIgnoredUserModels(userIds: List<String>) {
if (userIds.isEmpty()) {
noResultItem {
id("empty")
text(stringProvider.getString(R.string.no_ignored_users))
}
} else {
userIds.forEach { userId ->
ignoredUserItem {
id(userId)
userId(userId)
itemClickAction { callback?.onUserIdClicked(userId) }
}
}
}
}
interface Callback {
fun onUserIdClicked(userId: String)
}
}

View file

@ -0,0 +1,103 @@
/*
* 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.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel
data class IgnoredUsersViewState(
val ignoredUserIds: List<String> = 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()
.liveIgnoredUserIds()
.execute { async ->
copy(
ignoredUserIds = 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)
)
}
}
})
}
}

View file

@ -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
}
}
}

View file

@ -1,9 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/epoxyRecyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
app:itemSpacing="1dp"
tools:listitem="@layout/item_pushgateway" /> <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>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/itemIgnoredUserId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:lines="2"
android:maxLines="2"
android:minHeight="?listPreferredItemHeight"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
tools:text="@sample/matrix.json/data/mxid" />

View file

@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/waiting_view" android:id="@+id/waiting_view"

View file

@ -4,4 +4,6 @@
<!-- Strings not defined in Riot --> <!-- Strings not defined in Riot -->
<string name="notice_member_no_changes">"%1$s made no changes"</string> <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> </resources>

View file

@ -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>

View file

@ -37,10 +37,9 @@
<im.vector.riotx.core.preference.VectorPreference <im.vector.riotx.core.preference.VectorPreference
android:layout_width="match_parent" android:layout_width="match_parent"
android:enabled="@bool/false_not_implemented"
android:icon="@drawable/ic_settings_root_ignored_users" android:icon="@drawable/ic_settings_root_ignored_users"
android:title="@string/settings_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 <im.vector.riotx.core.preference.VectorPreference
android:layout_width="match_parent" android:layout_width="match_parent"