mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 12:18:48 +03:00
Merge pull request #4826 from vector-im/feature/bma/nick_color_final
Nick color
This commit is contained in:
commit
17e485fde1
12 changed files with 224 additions and 8 deletions
1
changelog.d/2614.feature
Normal file
1
changelog.d/2614.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Allow changing nick colors from the member detail screen
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.map
|
||||
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(
|
||||
val dummy: Boolean = false
|
||||
) : MavericksState
|
||||
|
||||
class UserColorAccountDataViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: DummyState,
|
||||
private val session: Session,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider
|
||||
) : VectorViewModel<DummyState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<UserColorAccountDataViewModel, DummyState> {
|
||||
override fun create(initialState: DummyState): UserColorAccountDataViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<UserColorAccountDataViewModel, DummyState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
observeAccountData()
|
||||
}
|
||||
|
||||
private fun observeAccountData() {
|
||||
session.flow()
|
||||
.liveUserAccountData(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)
|
||||
.unwrap()
|
||||
.map { it.content.toModel<Map<String, String>>() }
|
||||
.onEach { userColorAccountDataContent ->
|
||||
if (userColorAccountDataContent == null) {
|
||||
Timber.w("Invalid account data im.vector.setting.override_colors")
|
||||
}
|
||||
matrixItemColorProvider.setOverrideColors(userColorAccountDataContent)
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {
|
||||
// No op
|
||||
}
|
||||
}
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
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
|
||||
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
|
||||
|
@ -44,6 +46,42 @@ class MatrixItemColorProvider @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun setOverrideColors(overrideColors: Map<String, String>?) {
|
||||
cache.clear()
|
||||
overrideColors?.forEach {
|
||||
setOverrideColor(it.key, it.value)
|
||||
}
|
||||
}
|
||||
|
||||
fun setOverrideColor(id: String, colorSpec: String?): Boolean {
|
||||
val color = parseUserColorSpec(colorSpec)
|
||||
return if (color == null) {
|
||||
cache.remove(id)
|
||||
false
|
||||
} else {
|
||||
cache[id] = color
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
private fun parseUserColorSpec(colorText: String?): Int? {
|
||||
return if (colorText.isNullOrBlank()) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
if (colorText.length == 1) {
|
||||
colorProvider.getColor(getUserColorByIndex(colorText.toInt()))
|
||||
} else {
|
||||
Color.parseColor(colorText)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e, "Unable to parse color $colorText")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ColorRes
|
||||
@VisibleForTesting
|
||||
|
@ -52,7 +90,12 @@ class MatrixItemColorProvider @Inject constructor(
|
|||
|
||||
userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.code }
|
||||
|
||||
return when (abs(hash) % 8) {
|
||||
return getUserColorByIndex(abs(hash))
|
||||
}
|
||||
|
||||
@ColorRes
|
||||
private fun getUserColorByIndex(index: Int): Int {
|
||||
return when (index % 8) {
|
||||
1 -> R.color.element_name_02
|
||||
2 -> R.color.element_name_03
|
||||
3 -> R.color.element_name_04
|
||||
|
|
|
@ -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 newColorSpec: String) : RoomMemberProfileAction()
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ class RoomMemberProfileController @Inject constructor(
|
|||
fun onShowDeviceList()
|
||||
fun onShowDeviceListNoCrossSigning()
|
||||
fun onOpenDmClicked()
|
||||
fun onOverrideColorClicked()
|
||||
fun onJumpToReadReceiptClicked()
|
||||
fun onMentionClicked()
|
||||
fun onEditPowerLevel(currentRole: Role)
|
||||
|
@ -171,11 +172,20 @@ 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_nick_color),
|
||||
subtitle = state.userColorOverride,
|
||||
divider = !state.isMine,
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
|
@ -51,6 +52,7 @@ import im.vector.app.features.displayname.getBestName
|
|||
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
|
||||
|
@ -68,7 +70,8 @@ data class RoomMemberProfileArgs(
|
|||
class RoomMemberProfileFragment @Inject constructor(
|
||||
private val roomMemberProfileController: RoomMemberProfileController,
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
|
||||
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider
|
||||
) : VectorBaseFragment<FragmentMatrixProfileBinding>(),
|
||||
RoomMemberProfileController.Callback {
|
||||
|
||||
|
@ -200,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)
|
||||
|
@ -321,6 +325,26 @@ class RoomMemberProfileFragment @Inject constructor(
|
|||
navigator.openBigImageViewer(requireActivity(), view, userMatrixItem)
|
||||
}
|
||||
|
||||
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)
|
||||
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 newColor = views.editText.text.toString()
|
||||
if (newColor != state.userColorOverride) {
|
||||
viewModel.handle(RoomMemberProfileAction.SetUserColorOverride(newColor))
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.action_cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onEditPowerLevel(currentRole: Role) {
|
||||
EditPowerLevelDialogs.showChoice(requireActivity(), R.string.power_level_edit_title, currentRole) { newPowerLevel ->
|
||||
viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true))
|
||||
|
|
|
@ -28,10 +28,12 @@ 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
|
||||
|
@ -42,8 +44,10 @@ 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 +61,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<RoomMemberProfileViewState, RoomMemberProfileAction, RoomMemberProfileViewEvents>(initialState) {
|
||||
class RoomMemberProfileViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: RoomMemberProfileViewState,
|
||||
private val stringProvider: StringProvider,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||
private val session: Session
|
||||
) : VectorViewModel<RoomMemberProfileViewState, RoomMemberProfileAction, RoomMemberProfileViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<RoomMemberProfileViewModel, RoomMemberProfileViewState> {
|
||||
|
@ -85,6 +91,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 +128,21 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeAccountData() {
|
||||
session.flow()
|
||||
.liveUserAccountData(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)
|
||||
.unwrap()
|
||||
.onEach {
|
||||
val newUserColor = it.content.toModel<Map<String, String>>()?.get(initialState.userId)
|
||||
setState {
|
||||
copy(
|
||||
userColorOverride = newUserColor
|
||||
)
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun observeIgnoredState() {
|
||||
session.flow().liveIgnoredUsers()
|
||||
.map { ignored ->
|
||||
|
@ -143,6 +165,31 @@ 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
|
||||
?.toModel<Map<String, String>>()
|
||||
.orEmpty()
|
||||
.toMutableMap()
|
||||
if (matrixItemColorProvider.setOverrideColor(initialState.userId, action.newColorSpec)) {
|
||||
newOverrideColorSpecs[initialState.userId] = action.newColorSpec
|
||||
} else {
|
||||
newOverrideColorSpecs.remove(initialState.userId)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
session.accountDataService().updateUserAccountData(
|
||||
type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS,
|
||||
content = newOverrideColorSpecs
|
||||
)
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(RoomMemberProfileViewEvents.Failure(failure))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ data class RoomMemberProfileViewState(
|
|||
val allDevicesAreCrossSignedTrusted: Boolean = false,
|
||||
val asyncMembership: Async<Membership> = Uninitialized,
|
||||
val hasReadReceipt: Boolean = false,
|
||||
val userColorOverride: String? = null,
|
||||
val actionPermissions: ActionPermissions = ActionPermissions()
|
||||
) : MavericksState {
|
||||
|
||||
|
|
|
@ -2796,6 +2796,8 @@
|
|||
<string name="direct_room_profile_section_more_leave">Leave</string>
|
||||
<string name="room_profile_leaving_room">"Leaving the room…"</string>
|
||||
|
||||
<string name="room_member_override_nick_color">Override nick color</string>
|
||||
|
||||
<string name="room_member_power_level_admins">Admins</string>
|
||||
<string name="room_member_power_level_moderators">Moderators</string>
|
||||
<string name="room_member_power_level_custom">Custom</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue