diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index 26e203b4d7..aea029bff3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -739,7 +739,7 @@ class ConversationSettingsFragment : DSLSettingsFragment( customPref( RecipientPreference.Model( recipient = group, - onClick = { + onRowClick = { CommunicationActions.startConversation(requireActivity(), group, null) requireActivity().finish() } @@ -787,13 +787,26 @@ class ConversationSettingsFragment : DSLSettingsFragment( ) for (member in groupState.members) { + val canSetMemberLabel = member.member.isSelf && groupState.canSetOwnMemberLabel + val memberLabel = member.getMemberLabel(groupState) + customPref( RecipientPreference.Model( recipient = member.member, isAdmin = member.isAdmin, - memberLabel = member.getMemberLabel(groupState), + memberLabel = memberLabel, + canSetMemberLabel = canSetMemberLabel, lifecycleOwner = viewLifecycleOwner, - onClick = { + onRowClick = { + if (canSetMemberLabel && memberLabel == null) { + val action = ConversationSettingsFragmentDirections + .actionConversationSettingsFragmentToMemberLabelFragment(groupState.groupId) + navController.safeNavigate(action) + } else { + RecipientBottomSheetDialogFragment.show(parentFragmentManager, member.member.id, groupState.groupId) + } + }, + onAvatarClick = { RecipientBottomSheetDialogFragment.show(parentFragmentManager, member.member.id, groupState.groupId) } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsState.kt index eabe5801c8..ba5936c6df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsState.kt @@ -84,7 +84,8 @@ sealed class SpecificSettingsState { val membershipCountDescription: String = "", val legacyGroupState: LegacyGroupPreference.State = LegacyGroupPreference.State.NONE, val isAnnouncementGroup: Boolean = false, - val memberLabelsByRecipientId: Map = emptyMap() + val memberLabelsByRecipientId: Map = emptyMap(), + val canSetOwnMemberLabel: Boolean = false ) : SpecificSettingsState() { override val isLoaded: Boolean = groupTitleLoaded && groupDescriptionLoaded diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt index bc4eab23eb..cf38d44e21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt @@ -362,6 +362,7 @@ sealed class ConversationSettingsViewModel( if (groupId.isV2) { loadMemberLabels(groupId.requireV2(), fullMembers) + loadCanSetMemberLabel(groupId.requireV2()) } state.copy( @@ -520,6 +521,17 @@ sealed class ConversationSettingsViewModel( ) } } + + private fun loadCanSetMemberLabel(v2GroupId: GroupId.V2) = viewModelScope.launch(SignalDispatchers.IO) { + val canSetLabel = MemberLabelRepository.instance.canSetLabel(v2GroupId, Recipient.self()) + store.update { + it.copy( + specificSettingsState = it.requireGroupSettingsState().copy( + canSetOwnMemberLabel = canSetLabel + ) + ) + } + } } class Factory( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/RecipientPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/RecipientPreference.kt index eedc9c26b0..51b4421902 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/RecipientPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/RecipientPreference.kt @@ -36,8 +36,10 @@ object RecipientPreference { val recipient: Recipient, val isAdmin: Boolean = false, val memberLabel: StyledMemberLabel? = null, + val canSetMemberLabel: Boolean = false, val lifecycleOwner: LifecycleOwner? = null, - val onClick: (() -> Unit)? = null + val onRowClick: (() -> Unit)? = null, + val onAvatarClick: (() -> Unit)? = null ) : PreferenceModel() { override fun areItemsTheSame(newItem: Model): Boolean { return recipient.id == newItem.recipient.id @@ -47,7 +49,8 @@ object RecipientPreference { return super.areContentsTheSame(newItem) && recipient.hasSameContent(newItem.recipient) && isAdmin == newItem.isAdmin && - memberLabel == newItem.memberLabel + memberLabel == newItem.memberLabel && + canSetMemberLabel == newItem.canSetMemberLabel } } @@ -56,28 +59,36 @@ object RecipientPreference { private val name: TextView = itemView.findViewById(R.id.recipient_name) private val about: TextView? = itemView.findViewById(R.id.recipient_about) private val memberLabelView: MemberLabelPillView? = itemView.findViewById(R.id.recipient_member_label) + private val addMemberLabelView: TextView? = itemView.findViewById(R.id.add_member_label) private val admin: View? = itemView.findViewById(R.id.admin) private val badge: BadgeImageView = itemView.findViewById(R.id.recipient_badge) private var recipient: Recipient? = null + private var canSetMemberLabel: Boolean = false private val recipientObserver = Observer { recipient -> - onRecipientChanged(recipient) + onRecipientChanged(recipient = recipient, memberLabel = null, canSetMemberLabel = canSetMemberLabel) } override fun bind(model: Model) { - if (model.onClick != null) { - itemView.setOnClickListener { model.onClick.invoke() } + if (model.onRowClick != null) { + itemView.setOnClickListener { model.onRowClick.invoke() } } else { itemView.setOnClickListener(null) } + if (model.onAvatarClick != null) { + avatar.setOnClickListener { model.onAvatarClick.invoke() } + } else { + avatar.setOnClickListener(null) + } + + canSetMemberLabel = model.canSetMemberLabel + if (model.lifecycleOwner != null) { observeRecipient(model.lifecycleOwner, model.recipient) - model.memberLabel?.let(::showMemberLabel) - } else { - onRecipientChanged(model.recipient, model.memberLabel) } + onRecipientChanged(model.recipient, model.memberLabel, model.canSetMemberLabel) admin?.visible = model.isAdmin } @@ -86,7 +97,7 @@ object RecipientPreference { unbind() } - private fun onRecipientChanged(recipient: Recipient, memberLabel: StyledMemberLabel? = null) { + private fun onRecipientChanged(recipient: Recipient, memberLabel: StyledMemberLabel? = null, canSetMemberLabel: Boolean = false) { avatar.setRecipient(recipient) badge.setBadgeFromRecipient(recipient) name.text = if (recipient.isSelf) { @@ -104,17 +115,17 @@ object RecipientPreference { } } + val aboutText = recipient.combinedAboutAndEmoji when { memberLabel != null -> showMemberLabel(memberLabel) - !recipient.combinedAboutAndEmoji.isNullOrEmpty() -> { - about?.text = recipient.combinedAboutAndEmoji - about?.visible = true - memberLabelView?.visible = false - } + recipient.isSelf && canSetMemberLabel -> showAddMemberLabel() + + !aboutText.isNullOrBlank() -> showAbout(aboutText) else -> { memberLabelView?.visible = false + addMemberLabelView?.visible = false about?.visible = false } } @@ -131,9 +142,24 @@ object RecipientPreference { visible = true } + addMemberLabelView?.visible = false about?.visible = false } + private fun showAddMemberLabel() { + addMemberLabelView?.visible = true + memberLabelView?.visible = false + about?.visible = false + } + + private fun showAbout(text: String) { + about?.text = text + about?.visible = true + + memberLabelView?.visible = false + addMemberLabelView?.visible = false + } + private fun observeRecipient(lifecycleOwner: LifecycleOwner?, recipient: Recipient?) { this.recipient?.live()?.liveData?.removeObserver(recipientObserver) diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/memberlabel/MemberLabelRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/memberlabel/MemberLabelRepository.kt index 8c470b1948..3d2154f323 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/memberlabel/MemberLabelRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/memberlabel/MemberLabelRepository.kt @@ -52,6 +52,12 @@ class MemberLabelRepository private constructor( @WorkerThread fun getLabelJava(groupId: GroupId.V2, recipient: Recipient): MemberLabel? = runBlocking { getLabel(groupId, recipient) } + /** + * Checks whether the [Recipient] has permission to set their member label in the given group (blocking version for Java compatibility). + */ + @WorkerThread + fun canSetLabelJava(groupId: GroupId.V2, recipient: Recipient): Boolean = runBlocking { canSetLabel(groupId, recipient) } + /** * Gets the member label for a specific recipient in the group. */ diff --git a/app/src/main/res/layout/group_recipient_list_item.xml b/app/src/main/res/layout/group_recipient_list_item.xml index 053248534d..639e196c5a 100644 --- a/app/src/main/res/layout/group_recipient_list_item.xml +++ b/app/src/main/res/layout/group_recipient_list_item.xml @@ -55,7 +55,7 @@ android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.BodyLarge" app:layout_constrainedWidth="true" - app:layout_constraintBottom_toTopOf="@+id/recipient_member_label" + app:layout_constraintBottom_toTopOf="@+id/recipient_info_container" app:layout_constraintEnd_toStartOf="@+id/admin" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toEndOf="@+id/recipient_avatar" @@ -63,47 +63,57 @@ app:layout_constraintVertical_chainStyle="packed" tools:text="Miles Morales" /> - + app:layout_constraintStart_toEndOf="@+id/recipient_avatar" + app:layout_constraintTop_toBottomOf="@+id/recipient_name"> + + + + + + + + - - + app:constraint_referenced_ids="recipient_name,recipient_info_container" /> Approve Deny + + Add member label