From 7aaebd493be4c2f3996214089910da0210a4a723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Sun, 3 Jan 2021 18:47:53 +0100 Subject: [PATCH 01/16] [issue-2610] implement setting to override nick color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - allow changing the nick color by clicking the dispay-name in the room member detail page. - the ovirride-color can be specified as a hex string (#rrggbb) or as palette index (2) - entering an invalid color code or leaving the field blank reverts to the default hash-based nick color - the setting is stored in `account_data` as `im.vector.setting.override_colors` - future improvements / notes: - replace the text-based color entry with a proper color picker dialog - make the feature more discoverable - the color change listener is now in AppStateHandler, not sure if this is the best place - implement override color support in element-web / element-desktop, too Signed-off-by: Péter Radics --- CHANGES.md | 1 + .../accountdata/UserAccountDataTypes.kt | 1 + .../java/im/vector/app/AppStateHandler.kt | 22 +++++++++- .../helper/MatrixItemColorProvider.kt | 40 ++++++++++++++++- .../RoomMemberProfileFragment.kt | 44 ++++++++++++++++++- vector/src/main/res/values/strings.xml | 2 + 6 files changed, 107 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a028ef6f1e3..579124ce451 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ Changes in Element 1.0.14 (2020-XX-XX) =================================================== Features ✨: + - Allow changing nick colors (#2610) - Enable url previews for notices (#2562) Improvements 🙌: diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt index 69b15ff7d4c..91167d896f0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt @@ -27,4 +27,5 @@ object UserAccountDataTypes { const val TYPE_ALLOWED_WIDGETS = "im.vector.setting.allowed_widgets" const val TYPE_IDENTITY_SERVER = "m.identity_server" const val TYPE_ACCEPTED_TERMS = "m.accepted_terms" + const val TYPE_OVERRIDE_COLORS = "im.vector.setting.override_colors" } diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 1e92f7bc67c..b2791c0978e 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -23,12 +23,15 @@ import arrow.core.Option import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.HomeRoomListDataSource +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.list.ChronologicalRoomComparator import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.functions.BiFunction import io.reactivex.rxkotlin.addTo +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams @@ -46,13 +49,15 @@ class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val homeRoomListDataSource: HomeRoomListDataSource, private val selectedGroupDataSource: SelectedGroupDataSource, - private val chronologicalRoomComparator: ChronologicalRoomComparator) : LifecycleObserver { + private val chronologicalRoomComparator: ChronologicalRoomComparator, + private val matrixItemColorProvider: MatrixItemColorProvider) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { observeRoomsAndGroup() + observeUserAccountData() } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -93,4 +98,19 @@ class AppStateHandler @Inject constructor( } .addTo(compositeDisposable) } + + private fun observeUserAccountData() { + sessionDataSource.observe() + .observeOn(AndroidSchedulers.mainThread()) + .switchMap { + it.orNull()?.rx()?.liveAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) + ?: Observable.just(emptyList()) + } + .distinctUntilChanged() + .subscribe { + val overrideColorSpecs = it?.first()?.content?.toModel>() + matrixItemColorProvider.setOverrideColors(overrideColorSpecs) + } + .addTo(compositeDisposable) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt index 6a590206cb8..dcbfb13dcce 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt @@ -44,6 +44,39 @@ class MatrixItemColorProvider @Inject constructor( } } + fun setOverrideColors(overrideColors: Map?) { + overrideColors?.forEach() { + setOverrideColor(it.key, it.value) + } + } + + fun setOverrideColor(id: String, colorSpec: String?) : Boolean { + val color = parseUserColorSpec(colorSpec) + if (color == null) { + cache.remove(id) + return false + } else { + cache.put(id, color) + return true + } + } + + @ColorInt + private fun parseUserColorSpec(colorText: String?): Int? { + if (colorText.isNullOrBlank()) { + return null + } + try { + if (colorText.first() == '#') { + return (colorText.substring(1).toLong(radix = 16) or 0xff000000L).toInt() + } else { + return colorProvider.getColor(getUserColorByIndex(colorText.toInt())) + } + } catch (e: Throwable) { + return null + } + } + companion object { @ColorRes @VisibleForTesting @@ -52,7 +85,12 @@ class MatrixItemColorProvider @Inject constructor( userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() } - return when (abs(hash) % 8) { + return getUserColorByIndex(abs(hash)) + } + + @ColorRes + private fun getUserColorByIndex(index: Int): Int { + return when (index % 8) { 1 -> R.color.riotx_username_2 2 -> R.color.riotx_username_3 3 -> R.color.riotx_username_4 diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 08a251834e4..3c0ff15fb83 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -43,6 +43,7 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startSharePlainTextIntent +import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.DialogShareQrCodeBinding import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding @@ -50,9 +51,11 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailPendingAction import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -67,7 +70,8 @@ class RoomMemberProfileFragment @Inject constructor( val viewModelFactory: RoomMemberProfileViewModel.Factory, private val roomMemberProfileController: RoomMemberProfileController, private val avatarRenderer: AvatarRenderer, - private val roomDetailPendingActionStore: RoomDetailPendingActionStore + private val roomDetailPendingActionStore: RoomDetailPendingActionStore, + private val matrixItemColorProvider: MatrixItemColorProvider ) : VectorBaseFragment(), RoomMemberProfileController.Callback { @@ -199,6 +203,7 @@ class RoomMemberProfileFragment @Inject constructor( headerViews.memberProfileIdView.text = userMatrixItem.id val bestName = userMatrixItem.getBestName() headerViews.memberProfileNameView.text = bestName + headerViews.memberProfileNameView.setTextColor(matrixItemColorProvider.getColor(userMatrixItem)) views.matrixProfileToolbarTitleView.text = bestName avatarRenderer.render(userMatrixItem, headerViews.memberProfileAvatarView) avatarRenderer.render(userMatrixItem, views.matrixProfileToolbarAvatarImageView) @@ -237,6 +242,9 @@ class RoomMemberProfileFragment @Inject constructor( headerViews.memberProfileAvatarView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } + headerViews.memberProfileNameView.setOnClickListener { _ -> + onProfileNameClicked(userMatrixItem) + } views.matrixProfileToolbarAvatarImageView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } @@ -323,6 +331,40 @@ class RoomMemberProfileFragment @Inject constructor( navigator.openBigImageViewer(requireActivity(), view, userMatrixItem) } + private fun onProfileNameClicked(userMatrixItem: MatrixItem) { + val inflater = requireActivity().layoutInflater + val layout = inflater.inflate(R.layout.dialog_base_edit_text, null) + val views = DialogBaseEditTextBinding.bind(layout) + val session = injector().activeSessionHolder().getActiveSession() + val overrideColorsSetting = session.getAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + val overrideColorSpecs = overrideColorsSetting?.content?.toMap().orEmpty() + val overrideColorSpec = overrideColorSpecs[userMatrixItem.id]?.toString() + views.editText.setText(overrideColorSpec) + views.editText.hint = "#000000" + + AlertDialog.Builder(requireActivity()) + .setTitle(R.string.room_member_override_color) + .setView(layout) + .setPositiveButton(R.string.ok) { _, _ -> + val newOverrideColorSpec = views.editText.text.toString() + if (newOverrideColorSpec != overrideColorSpec) { + val newOverrideColorSpecs = overrideColorSpecs.toMutableMap() + if (matrixItemColorProvider.setOverrideColor(userMatrixItem.id, newOverrideColorSpec)) { + newOverrideColorSpecs[userMatrixItem.id] = newOverrideColorSpec + } else { + newOverrideColorSpecs.remove(userMatrixItem.id) + } + session.updateAccountData( + type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, + content = newOverrideColorSpecs + ) + headerViews.memberProfileNameView.setTextColor(matrixItemColorProvider.getColor(userMatrixItem)) + } + } + .setNegativeButton(R.string.cancel, null) + .show() + } + override fun onEditPowerLevel(currentRole: Role) { EditPowerLevelDialogs.showChoice(requireActivity(), currentRole) { newPowerLevel -> viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true)) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 76541460d2a..61bc5dec945 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2241,6 +2241,8 @@ Leave "Leaving the room…" + Override color + Admins Moderators Custom From 889a9a17983a4241a285d3d12aeee1e6fb7a4d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Sun, 3 Jan 2021 21:42:33 +0100 Subject: [PATCH 02/16] [issue-2610] fix NoSuchElement error --- vector/src/main/java/im/vector/app/AppStateHandler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index b2791c0978e..303433db677 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -108,7 +108,7 @@ class AppStateHandler @Inject constructor( } .distinctUntilChanged() .subscribe { - val overrideColorSpecs = it?.first()?.content?.toModel>() + val overrideColorSpecs = it?.firstOrNull()?.content?.toModel>() matrixItemColorProvider.setOverrideColors(overrideColorSpecs) } .addTo(compositeDisposable) From 0e400dca24a69636059bf23b03068cfe40197709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Wed, 3 Feb 2021 20:04:24 +0100 Subject: [PATCH 03/16] [issue-2610] Add Override Color menu item under More... --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../RoomMemberProfileController.kt | 13 +++++++++++-- .../RoomMemberProfileFragment.kt | 15 ++++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 247e2b90ad4..73f88221b13 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ #Fri Jan 15 11:30:47 CET 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a7ca23b3ccf265680f2bfd35f1f00b1424f4466292c7337c85d46c9641b3f053 +#distributionSha256Sum=a7ca23b3ccf265680f2bfd35f1f00b1424f4466292c7337c85d46c9641b3f053 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index e29c197ab85..9c2d2ded70e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -46,6 +46,7 @@ class RoomMemberProfileController @Inject constructor( fun onShowDeviceList() fun onShowDeviceListNoCrossSigning() fun onOpenDmClicked() + fun onOverrideColorClicked() fun onJumpToReadReceiptClicked() fun onMentionClicked() fun onEditPowerLevel(currentRole: Role) @@ -180,11 +181,19 @@ class RoomMemberProfileController @Inject constructor( private fun buildMoreSection(state: RoomMemberProfileViewState) { // More + buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + + buildProfileAction( + id = "overrideColor", + editable = false, + title = stringProvider.getString(R.string.room_member_override_color), + dividerColor = dividerColor, + action = { callback?.onOverrideColorClicked() } + ) + if (!state.isMine) { val membership = state.asyncMembership() ?: return - buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) - buildProfileAction( id = "direct", editable = false, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 92cdf305fe3..2099b706eb7 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -243,7 +243,7 @@ class RoomMemberProfileFragment @Inject constructor( onAvatarClicked(view, userMatrixItem) } headerViews.memberProfileNameView.setOnClickListener { _ -> - onProfileNameClicked(userMatrixItem) + onOverrideColorClicked() } views.matrixProfileToolbarAvatarImageView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) @@ -331,14 +331,15 @@ class RoomMemberProfileFragment @Inject constructor( navigator.openBigImageViewer(requireActivity(), view, userMatrixItem) } - private fun onProfileNameClicked(userMatrixItem: MatrixItem) { + override fun onOverrideColorClicked(): Unit = withState(viewModel) { state -> val inflater = requireActivity().layoutInflater val layout = inflater.inflate(R.layout.dialog_base_edit_text, null) val views = DialogBaseEditTextBinding.bind(layout) val session = injector().activeSessionHolder().getActiveSession() val overrideColorsSetting = session.getAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) val overrideColorSpecs = overrideColorsSetting?.content?.toMap().orEmpty() - val overrideColorSpec = overrideColorSpecs[userMatrixItem.id]?.toString() + val userId = state.userId; + val overrideColorSpec = overrideColorSpecs[userId]?.toString() views.editText.setText(overrideColorSpec) views.editText.hint = "#000000" @@ -349,16 +350,16 @@ class RoomMemberProfileFragment @Inject constructor( val newOverrideColorSpec = views.editText.text.toString() if (newOverrideColorSpec != overrideColorSpec) { val newOverrideColorSpecs = overrideColorSpecs.toMutableMap() - if (matrixItemColorProvider.setOverrideColor(userMatrixItem.id, newOverrideColorSpec)) { - newOverrideColorSpecs[userMatrixItem.id] = newOverrideColorSpec + if (matrixItemColorProvider.setOverrideColor(userId, newOverrideColorSpec)) { + newOverrideColorSpecs[userId] = newOverrideColorSpec } else { - newOverrideColorSpecs.remove(userMatrixItem.id) + newOverrideColorSpecs.remove(userId) } session.updateAccountData( type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, content = newOverrideColorSpecs ) - headerViews.memberProfileNameView.setTextColor(matrixItemColorProvider.getColor(userMatrixItem)) + invalidate() } } .setNegativeButton(R.string.cancel, null) From f7d8127fa669dc1fe4d4ac5b195b0a4cd19779f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Wed, 3 Feb 2021 21:04:47 +0100 Subject: [PATCH 04/16] [issue-2610] remove extra semicolon --- .../app/features/roommemberprofile/RoomMemberProfileFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 2099b706eb7..a60b7368fad 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -338,7 +338,7 @@ class RoomMemberProfileFragment @Inject constructor( val session = injector().activeSessionHolder().getActiveSession() val overrideColorsSetting = session.getAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) val overrideColorSpecs = overrideColorsSetting?.content?.toMap().orEmpty() - val userId = state.userId; + val userId = state.userId val overrideColorSpec = overrideColorSpecs[userId]?.toString() views.editText.setText(overrideColorSpec) views.editText.hint = "#000000" From cc15f9b1298d0da8c39afa71b7e19fc5d58d8f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Sat, 6 Feb 2021 09:34:31 +0100 Subject: [PATCH 05/16] [issue-2610] remove click handler from display-name Remove the click handler that opens the override color dialog from the display-name part on the member profile page, as this is not really discoverable and we have a proper menu item for it now. --- .../features/roommemberprofile/RoomMemberProfileFragment.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index a60b7368fad..e50645ae01a 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -242,9 +242,6 @@ class RoomMemberProfileFragment @Inject constructor( headerViews.memberProfileAvatarView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } - headerViews.memberProfileNameView.setOnClickListener { _ -> - onOverrideColorClicked() - } views.matrixProfileToolbarAvatarImageView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } From db97046f082a0417f2195b0dd0ffcef724352569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Radics?= Date: Sat, 13 Feb 2021 13:30:31 +0100 Subject: [PATCH 06/16] [issue-2610] change menu text to "Override nick color" --- .../features/roommemberprofile/RoomMemberProfileController.kt | 2 +- .../app/features/roommemberprofile/RoomMemberProfileFragment.kt | 2 +- vector/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 2a01e45f93e..ba114e2e054 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -184,7 +184,7 @@ class RoomMemberProfileController @Inject constructor( buildProfileAction( id = "overrideColor", editable = false, - title = stringProvider.getString(R.string.room_member_override_color), + title = stringProvider.getString(R.string.room_member_override_nick_color), dividerColor = dividerColor, action = { callback?.onOverrideColorClicked() } ) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index e50645ae01a..04ee40668ec 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -341,7 +341,7 @@ class RoomMemberProfileFragment @Inject constructor( views.editText.hint = "#000000" AlertDialog.Builder(requireActivity()) - .setTitle(R.string.room_member_override_color) + .setTitle(R.string.room_member_override_nick_color) .setView(layout) .setPositiveButton(R.string.ok) { _, _ -> val newOverrideColorSpec = views.editText.text.toString() diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 2b98efc7651..085c9997136 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2273,7 +2273,7 @@ Leave "Leaving the room…" - Override color + Override nick color Admins Moderators From 1d3cc52991a749bc06a20eb2594c946df8a14416 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 13:05:06 +0100 Subject: [PATCH 07/16] Changelog new management --- CHANGES.md | 1 - changelog.d/2614.feature | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog.d/2614.feature diff --git a/CHANGES.md b/CHANGES.md index be4ee5342f0..e0dd3298d8e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -581,7 +581,6 @@ Changes in Element 1.1.7 (2021-05-12) =================================================== Features ✨: - - Allow changing nick colors (#2610) - Spaces beta Improvements 🙌: diff --git a/changelog.d/2614.feature b/changelog.d/2614.feature new file mode 100644 index 00000000000..7feceeabba0 --- /dev/null +++ b/changelog.d/2614.feature @@ -0,0 +1 @@ +Allow changing nick colors from the member detail screen \ No newline at end of file From 364457d102689340d40a8c4261725b25bb7573bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 13:13:48 +0100 Subject: [PATCH 08/16] Move logic to dedicated ViewModel --- .../java/im/vector/app/AppStateHandler.kt | 29 -------- .../vector/app/features/home/HomeActivity.kt | 2 + .../home/UserColorAccountDataViewModel.kt | 73 +++++++++++++++++++ 3 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index afd4c53c379..9ed9dd5b23a 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -21,13 +21,8 @@ import androidx.lifecycle.LifecycleOwner import arrow.core.Option import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.BehaviorDataSource -import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.session.coroutineScope import im.vector.app.features.ui.UiStateRepository -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -38,11 +33,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.rx.rx import javax.inject.Inject import javax.inject.Singleton @@ -62,7 +54,6 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr @Singleton class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, - private val matrixItemColorProvider: MatrixItemColorProvider, private val uiStateRepository: UiStateRepository, private val activeSessionHolder: ActiveSessionHolder ) : DefaultLifecycleObserver { @@ -141,11 +132,6 @@ class AppStateHandler @Inject constructor( return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.ByLegacyGroup)?.groupSummary?.groupId } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun entersForeground() { - observeUserAccountData() - } - override fun onResume(owner: LifecycleOwner) { observeActiveSession() } @@ -164,19 +150,4 @@ class AppStateHandler @Inject constructor( } } } - - private fun observeUserAccountData() { - sessionDataSource.observe() - .observeOn(AndroidSchedulers.mainThread()) - .switchMap { - it.orNull()?.rx()?.liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) - ?: Observable.just(emptyList()) - } - .distinctUntilChanged() - .subscribe { - val overrideColorSpecs = it?.firstOrNull()?.content?.toModel>() - matrixItemColorProvider.setOverrideColors(overrideColorSpecs) - } - .addTo(compositeDisposable) - } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index e2696115f4b..43456f16bb9 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -106,6 +106,8 @@ class HomeActivity : private val homeActivityViewModel: HomeActivityViewModel by viewModel() @Suppress("UNUSED") private val analyticsAccountDataViewModel: AnalyticsAccountDataViewModel by viewModel() + @Suppress("UNUSED") + private val userColorAccountDataViewModel: UserColorAccountDataViewModel by viewModel() private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel() private val promoteRestrictedViewModel: PromoteRestrictedViewModel by viewModel() diff --git a/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt new file mode 100644 index 00000000000..9e13fe56d00 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home + +import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.flow.flow + +data class DummyState( + val dummy: Boolean = false +) : MavericksState + +class UserColorAccountDataViewModel @AssistedInject constructor( + @Assisted initialState: DummyState, + private val session: Session, + private val matrixItemColorProvider: MatrixItemColorProvider +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DummyState): UserColorAccountDataViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + init { + observeAccountData() + } + + private fun observeAccountData() { + session.flow() + .liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) + .mapNotNull { it.firstOrNull() } + .mapNotNull { it.content.toModel>() } + .onEach { userColorAccountDataContent -> + matrixItemColorProvider.setOverrideColors(userColorAccountDataContent) + } + .launchIn(viewModelScope) + } + + override fun handle(action: EmptyAction) { + // No op + } +} From 07d2a15cf847222329fdf2074f08cdc429a0eb36 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 14:45:35 +0100 Subject: [PATCH 09/16] Code cleanup --- .../home/UserColorAccountDataViewModel.kt | 7 +++- .../helper/MatrixItemColorProvider.kt | 36 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt index 9e13fe56d00..77f6eade4a4 100644 --- a/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt @@ -28,12 +28,14 @@ import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.flow.flow +import timber.log.Timber data class DummyState( val dummy: Boolean = false @@ -60,8 +62,11 @@ class UserColorAccountDataViewModel @AssistedInject constructor( session.flow() .liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) .mapNotNull { it.firstOrNull() } - .mapNotNull { it.content.toModel>() } + .map { it.content.toModel>() } .onEach { userColorAccountDataContent -> + if (userColorAccountDataContent == null) { + Timber.w("Invalid account data im.vector.setting.override_colors") + } matrixItemColorProvider.setOverrideColors(userColorAccountDataContent) } .launchIn(viewModelScope) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt index 97028c47f26..4c99894bf35 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt @@ -22,6 +22,7 @@ import androidx.annotation.VisibleForTesting import im.vector.app.R import im.vector.app.core.resources.ColorProvider import org.matrix.android.sdk.api.util.MatrixItem +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton import kotlin.math.abs @@ -45,35 +46,38 @@ class MatrixItemColorProvider @Inject constructor( } fun setOverrideColors(overrideColors: Map?) { - overrideColors?.forEach() { + cache.clear() + overrideColors?.forEach { setOverrideColor(it.key, it.value) } } - fun setOverrideColor(id: String, colorSpec: String?) : Boolean { + fun setOverrideColor(id: String, colorSpec: String?): Boolean { val color = parseUserColorSpec(colorSpec) - if (color == null) { + return if (color == null) { cache.remove(id) - return false + false } else { - cache.put(id, color) - return true + cache[id] = color + true } } @ColorInt private fun parseUserColorSpec(colorText: String?): Int? { - if (colorText.isNullOrBlank()) { - return null - } - try { - if (colorText.first() == '#') { - return (colorText.substring(1).toLong(radix = 16) or 0xff000000L).toInt() - } else { - return colorProvider.getColor(getUserColorByIndex(colorText.toInt())) + return if (colorText.isNullOrBlank()) { + null + } else { + try { + if (colorText.first() == '#') { + (colorText.substring(1).toLong(radix = 16) or 0xff000000L).toInt() + } else { + colorProvider.getColor(getUserColorByIndex(colorText.toInt())) + } + } catch (e: Throwable) { + Timber.e(e, "Unable to parse color $colorText") + null } - } catch (e: Throwable) { - return null } } From ddadefdbd6d27cd7394797025254572dcbffedd5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 15:08:30 +0100 Subject: [PATCH 10/16] Move logic to ViewModel --- .../RoomMemberProfileAction.kt | 1 + .../RoomMemberProfileController.kt | 1 + .../RoomMemberProfileFragment.kt | 29 ++--------- .../RoomMemberProfileViewModel.kt | 51 +++++++++++++++++-- .../RoomMemberProfileViewState.kt | 1 + 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt index 7a171ca3e5c..1467889b4a7 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt @@ -28,4 +28,5 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object VerifyUser : RoomMemberProfileAction() object ShareRoomMemberProfile : RoomMemberProfileAction() data class SetPowerLevel(val previousValue: Int, val newValue: Int, val askForValidation: Boolean) : RoomMemberProfileAction() + data class SetUserColorOverride(val newColor: String) : RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 2aa3a08b53e..06ba18f4a7d 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -178,6 +178,7 @@ class RoomMemberProfileController @Inject constructor( id = "overrideColor", editable = false, title = stringProvider.getString(R.string.room_member_override_nick_color), + subtitle = state.userColorOverride, divider = false, action = { callback?.onOverrideColorClicked() } ) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 3d97ce10dd5..2bd93e41dac 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -24,7 +24,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success @@ -56,10 +55,8 @@ import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs -import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -328,36 +325,20 @@ class RoomMemberProfileFragment @Inject constructor( navigator.openBigImageViewer(requireActivity(), view, userMatrixItem) } - override fun onOverrideColorClicked(): Unit = withState(viewModel) { state -> + override fun onOverrideColorClicked(): Unit = withState(viewModel) { state -> val inflater = requireActivity().layoutInflater val layout = inflater.inflate(R.layout.dialog_base_edit_text, null) val views = DialogBaseEditTextBinding.bind(layout) - val session = injector().activeSessionHolder().getActiveSession() - val overrideColorsSetting = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) - val overrideColorSpecs = overrideColorsSetting?.content?.toMap().orEmpty() - val userId = state.userId - val overrideColorSpec : String? = overrideColorSpecs[userId]?.toString() - views.editText.setText(overrideColorSpec) + views.editText.setText(state.userColorOverride) views.editText.hint = "#000000" MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.room_member_override_nick_color) .setView(layout) .setPositiveButton(R.string.ok) { _, _ -> - val newOverrideColorSpec = views.editText.text.toString() - if (newOverrideColorSpec != overrideColorSpec) { - val newOverrideColorSpecs = overrideColorSpecs.toMutableMap() - if (matrixItemColorProvider.setOverrideColor(userId, newOverrideColorSpec)) { - newOverrideColorSpecs[userId] = newOverrideColorSpec - } else { - newOverrideColorSpecs.remove(userId) - } - viewModel.viewModelScope.launch { - session.accountDataService().updateUserAccountData( - type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, - content = newOverrideColorSpecs) - } - invalidate() + val newColor = views.editText.text.toString() + if (newColor != state.userColorOverride) { + viewModel.handle(RoomMemberProfileAction.SetUserColorOverride(newColor)) } } .setNegativeButton(R.string.cancel, null) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 5b07b101e76..8129dec3d4a 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -28,22 +28,27 @@ import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.displayname.getBestName +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.powerlevel.PowerLevelsFlowFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams @@ -57,10 +62,12 @@ import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.unwrap -class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, - private val stringProvider: StringProvider, - private val session: Session) : - VectorViewModel(initialState) { +class RoomMemberProfileViewModel @AssistedInject constructor( + @Assisted private val initialState: RoomMemberProfileViewState, + private val stringProvider: StringProvider, + private val matrixItemColorProvider: MatrixItemColorProvider, + private val session: Session +) : VectorViewModel(initialState) { @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { @@ -85,6 +92,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v ) } observeIgnoredState() + observeAccountData() viewModelScope.launch(Dispatchers.Main) { // Do we have a room member for this id. val roomMember = withContext(Dispatchers.Default) { @@ -121,6 +129,24 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } } + private fun observeAccountData() { + session.flow() + .liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) + .mapNotNull { it.firstOrNull() } + .map { it.content.toModel>() } + .map { userColorAccountDataContent -> + userColorAccountDataContent?.get(initialState.userId) + } + .onEach { + setState { + copy( + userColorOverride = it + ) + } + } + .launchIn(viewModelScope) + } + private fun observeIgnoredState() { session.flow().liveIgnoredUsers() .map { ignored -> @@ -143,6 +169,23 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v is RoomMemberProfileAction.BanOrUnbanUser -> handleBanOrUnbanAction(action) is RoomMemberProfileAction.KickUser -> handleKickAction(action) RoomMemberProfileAction.InviteUser -> handleInviteAction() + is RoomMemberProfileAction.SetUserColorOverride -> handleSetUserColorOverride(action) + }.exhaustive + } + + private fun handleSetUserColorOverride(action: RoomMemberProfileAction.SetUserColorOverride) { + val newOverrideColorSpecs = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + ?.content?.toMap().orEmpty().toMutableMap() + if (matrixItemColorProvider.setOverrideColor(initialState.userId, action.newColor)) { + newOverrideColorSpecs[initialState.userId] = action.newColor + } else { + newOverrideColorSpecs.remove(initialState.userId) + } + viewModelScope.launch { + session.accountDataService().updateUserAccountData( + type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, + content = newOverrideColorSpecs + ) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index a4730153c27..1f2c3d6ce4d 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -41,6 +41,7 @@ data class RoomMemberProfileViewState( val allDevicesAreCrossSignedTrusted: Boolean = false, val asyncMembership: Async = Uninitialized, val hasReadReceipt: Boolean = false, + val userColorOverride: String? = null, val actionPermissions: ActionPermissions = ActionPermissions() ) : MavericksState { From 1cb91ca5dfb16f586fdc2062b9bd633294882fc6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 15:13:01 +0100 Subject: [PATCH 11/16] Use color parser --- .../room/detail/timeline/helper/MatrixItemColorProvider.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt index 4c99894bf35..1c64082f654 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.helper +import android.graphics.Color import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.annotation.VisibleForTesting @@ -69,10 +70,10 @@ class MatrixItemColorProvider @Inject constructor( null } else { try { - if (colorText.first() == '#') { - (colorText.substring(1).toLong(radix = 16) or 0xff000000L).toInt() - } else { + if (colorText.length == 1) { colorProvider.getColor(getUserColorByIndex(colorText.toInt())) + } else { + Color.parseColor(colorText) } } catch (e: Throwable) { Timber.e(e, "Unable to parse color $colorText") From a7b72ed39d98b544a17fcf796c1f6eeab024a3ea Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 15:22:48 +0100 Subject: [PATCH 12/16] Fix latest small bugs --- .../vector/app/core/di/MavericksViewModelModule.kt | 6 ++++++ .../roommemberprofile/RoomMemberProfileAction.kt | 2 +- .../roommemberprofile/RoomMemberProfileController.kt | 2 +- .../roommemberprofile/RoomMemberProfileViewModel.kt | 12 ++++++++---- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index d09cd21d197..09e161bc99f 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -42,6 +42,7 @@ import im.vector.app.features.home.HomeDetailViewModel import im.vector.app.features.home.PromoteRestrictedViewModel import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel import im.vector.app.features.home.UnreadMessagesSharedViewModel +import im.vector.app.features.home.UserColorAccountDataViewModel import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsViewModel import im.vector.app.features.home.room.detail.RoomDetailViewModel import im.vector.app.features.home.room.detail.composer.MessageComposerViewModel @@ -412,6 +413,11 @@ interface MavericksViewModelModule { @MavericksViewModelKey(RoomMemberProfileViewModel::class) fun roomMemberProfileViewModelFactory(factory: RoomMemberProfileViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds + @IntoMap + @MavericksViewModelKey(UserColorAccountDataViewModel::class) + fun userColorAccountDataViewModelFactory(factory: UserColorAccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds @IntoMap @MavericksViewModelKey(RoomPreviewViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt index 1467889b4a7..87801a7e95c 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileAction.kt @@ -28,5 +28,5 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object VerifyUser : RoomMemberProfileAction() object ShareRoomMemberProfile : RoomMemberProfileAction() data class SetPowerLevel(val previousValue: Int, val newValue: Int, val askForValidation: Boolean) : RoomMemberProfileAction() - data class SetUserColorOverride(val newColor: String) : RoomMemberProfileAction() + data class SetUserColorOverride(val newColorSpec: String) : RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 06ba18f4a7d..860961ae29e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -179,7 +179,7 @@ class RoomMemberProfileController @Inject constructor( editable = false, title = stringProvider.getString(R.string.room_member_override_nick_color), subtitle = state.userColorOverride, - divider = false, + divider = !state.isMine, action = { callback?.onOverrideColorClicked() } ) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 8129dec3d4a..7cab5a1cc8a 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -174,10 +174,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor( } private fun handleSetUserColorOverride(action: RoomMemberProfileAction.SetUserColorOverride) { - val newOverrideColorSpecs = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) - ?.content?.toMap().orEmpty().toMutableMap() - if (matrixItemColorProvider.setOverrideColor(initialState.userId, action.newColor)) { - newOverrideColorSpecs[initialState.userId] = action.newColor + val newOverrideColorSpecs = session.accountDataService() + .getUserAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + ?.content + ?.toModel>() + .orEmpty() + .toMutableMap() + if (matrixItemColorProvider.setOverrideColor(initialState.userId, action.newColorSpec)) { + newOverrideColorSpecs[initialState.userId] = action.newColorSpec } else { newOverrideColorSpecs.remove(initialState.userId) } From 6d8b5db18e3c1a92d275972096776b2ab7e05c8f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 15:34:42 +0100 Subject: [PATCH 13/16] Fix latest small bugs --- .../roommemberprofile/RoomMemberProfileViewModel.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 7cab5a1cc8a..8961ab66cb3 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -186,10 +186,14 @@ class RoomMemberProfileViewModel @AssistedInject constructor( newOverrideColorSpecs.remove(initialState.userId) } viewModelScope.launch { - session.accountDataService().updateUserAccountData( - type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, - content = newOverrideColorSpecs - ) + try { + session.accountDataService().updateUserAccountData( + type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, + content = newOverrideColorSpecs + ) + } catch (failure: Throwable) { + _viewEvents.post(RoomMemberProfileViewEvents.Failure(failure)) + } } } From 96d5652fcd96971ded31e11d4eb47805bc82536d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 31 Dec 2021 15:48:08 +0100 Subject: [PATCH 14/16] Small cleanup --- .../app/features/home/UserColorAccountDataViewModel.kt | 6 +++--- .../roommemberprofile/RoomMemberProfileViewModel.kt | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt index 77f6eade4a4..3d4f219a7c8 100644 --- a/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UserColorAccountDataViewModel.kt @@ -29,12 +29,12 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber data class DummyState( @@ -60,8 +60,8 @@ class UserColorAccountDataViewModel @AssistedInject constructor( private fun observeAccountData() { session.flow() - .liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) - .mapNotNull { it.firstOrNull() } + .liveUserAccountData(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + .unwrap() .map { it.content.toModel>() } .onEach { userColorAccountDataContent -> if (userColorAccountDataContent == null) { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 8961ab66cb3..859bb6125f9 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -39,7 +39,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -131,8 +130,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor( private fun observeAccountData() { session.flow() - .liveUserAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) - .mapNotNull { it.firstOrNull() } + .liveUserAccountData(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + .unwrap() .map { it.content.toModel>() } .map { userColorAccountDataContent -> userColorAccountDataContent?.get(initialState.userId) From 51c9c2f032a7709d18b56bbfb35def25e3d08a24 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Jan 2022 14:56:17 +0100 Subject: [PATCH 15/16] Optimize call flow --- .../roommemberprofile/RoomMemberProfileViewModel.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 859bb6125f9..4f3d9d0776e 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -132,14 +132,11 @@ class RoomMemberProfileViewModel @AssistedInject constructor( session.flow() .liveUserAccountData(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) .unwrap() - .map { it.content.toModel>() } - .map { userColorAccountDataContent -> - userColorAccountDataContent?.get(initialState.userId) - } .onEach { + val newUserColor = it.content.toModel>()?.get(initialState.userId) setState { copy( - userColorOverride = it + userColorOverride = newUserColor ) } } From 02a8fd231c1e517a7cc6af1ea7127a0595ba64fd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 Jan 2022 16:35:07 +0100 Subject: [PATCH 16/16] Fix compilation issue after develop being merged. --- .../app/features/roommemberprofile/RoomMemberProfileFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 861de934153..0243b44a8c8 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -341,7 +341,7 @@ class RoomMemberProfileFragment @Inject constructor( viewModel.handle(RoomMemberProfileAction.SetUserColorOverride(newColor)) } } - .setNegativeButton(R.string.cancel, null) + .setNegativeButton(R.string.action_cancel, null) .show() }