mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-19 16:19:33 +01:00
Show member labels in conversation settings.
This commit is contained in:
committed by
Greyson Parrelli
parent
0199cd24ef
commit
d7b7727aa6
@@ -33,6 +33,7 @@ import org.signal.core.util.Result
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.signal.core.util.concurrent.addTo
|
||||
import org.signal.core.util.getParcelableArrayListExtraCompat
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.AvatarPreviewActivity
|
||||
import org.thoughtcrime.securesms.BlockUnblockDialog
|
||||
@@ -67,10 +68,13 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.R
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.preferences.SharedMediaPreference
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.preferences.Utils.formatMutedUntil
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.Colorizer
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog
|
||||
import org.thoughtcrime.securesms.groups.ui.addmembers.AddMembersActivity
|
||||
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity
|
||||
@@ -123,6 +127,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
private val args: ConversationSettingsFragmentArgs by navArgs()
|
||||
private val alertTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary) }
|
||||
private val alertDisabledTint by lazy { ContextCompat.getColor(requireContext(), R.color.signal_alert_primary_50) }
|
||||
private val colorizer = Colorizer()
|
||||
private val blockIcon by lazy {
|
||||
ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_block_24).apply {
|
||||
colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN)
|
||||
@@ -199,7 +204,9 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
|
||||
REQUEST_CODE_RETURN_FROM_MEDIA -> viewModel.refreshSharedMedia()
|
||||
|
||||
REQUEST_CODE_ADD_CONTACT -> viewModel.refreshRecipient()
|
||||
|
||||
REQUEST_CODE_VIEW_CONTACT -> viewModel.refreshRecipient()
|
||||
}
|
||||
}
|
||||
@@ -432,7 +439,13 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.CameraXFragment_allow_access_camera), getString(R.string.CameraXFragment_to_capture_photos_and_video_allow_camera), CoreUiR.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos), null, R.string.CameraXFragment_allow_access_camera, R.string.CameraXFragment_to_capture_photos_videos, getParentFragmentManager())
|
||||
.withPermanentDenialDialog(
|
||||
getString(R.string.CameraXFragment_signal_needs_camera_access_capture_photos),
|
||||
null,
|
||||
R.string.CameraXFragment_allow_access_camera,
|
||||
R.string.CameraXFragment_to_capture_photos_videos,
|
||||
getParentFragmentManager()
|
||||
)
|
||||
.onAllGranted { addToGroupStoryDelegate.addToStory(state.recipient.id) }
|
||||
.onAnyDenied { Toast.makeText(requireContext(), R.string.CameraXFragment_signal_needs_camera_access_capture_photos, Toast.LENGTH_LONG).show() }
|
||||
.execute()
|
||||
@@ -768,11 +781,16 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
)
|
||||
}
|
||||
|
||||
colorizer.onGroupMembershipChanged(
|
||||
serviceIds = groupState.allMembers.mapNotNull { it.member.serviceId.orNull() }
|
||||
)
|
||||
|
||||
for (member in groupState.members) {
|
||||
customPref(
|
||||
RecipientPreference.Model(
|
||||
recipient = member.member,
|
||||
isAdmin = member.isAdmin,
|
||||
memberLabel = member.getMemberLabel(groupState),
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
onClick = {
|
||||
RecipientBottomSheetDialogFragment.show(parentFragmentManager, member.member.id, groupState.groupId)
|
||||
@@ -949,6 +967,15 @@ class ConversationSettingsFragment : DSLSettingsFragment(
|
||||
}
|
||||
}
|
||||
|
||||
private fun GroupMemberEntry.FullMember.getMemberLabel(
|
||||
groupState: SpecificSettingsState.GroupSettingsState
|
||||
): StyledMemberLabel? {
|
||||
return groupState.memberLabelsByRecipientId[member.id]?.let { label ->
|
||||
val tintColor = colorizer.getIncomingGroupSenderColor(context = requireContext(), recipient = member)
|
||||
StyledMemberLabel(label, tintColor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDisappearingMessagesLifespan(disappearingMessagesLifespan: Int): String {
|
||||
return if (disappearingMessagesLifespan <= 0) {
|
||||
getString(R.string.preferences_off)
|
||||
|
||||
@@ -7,8 +7,10 @@ import org.thoughtcrime.securesms.database.MediaTable
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabel
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
data class ConversationSettingsState(
|
||||
val threadId: Long = -1,
|
||||
@@ -81,7 +83,8 @@ sealed class SpecificSettingsState {
|
||||
val groupLinkEnabled: Boolean = false,
|
||||
val membershipCountDescription: String = "",
|
||||
val legacyGroupState: LegacyGroupPreference.State = LegacyGroupPreference.State.NONE,
|
||||
val isAnnouncementGroup: Boolean = false
|
||||
val isAnnouncementGroup: Boolean = false,
|
||||
val memberLabelsByRecipientId: Map<RecipientId, MemberLabel> = emptyMap()
|
||||
) : SpecificSettingsState() {
|
||||
|
||||
override val isLoaded: Boolean = groupTitleLoaded && groupDescriptionLoaded
|
||||
|
||||
@@ -31,7 +31,9 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelRepository
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupAddMembersResult
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
|
||||
@@ -358,6 +360,10 @@ sealed class ConversationSettingsViewModel(
|
||||
val groupState = state.requireGroupSettingsState()
|
||||
val canShowMore = !groupState.groupMembersExpanded && fullMembers.size > 6
|
||||
|
||||
if (groupId.isV2) {
|
||||
loadMemberLabels(groupId.requireV2(), fullMembers)
|
||||
}
|
||||
|
||||
state.copy(
|
||||
specificSettingsState = groupState.copy(
|
||||
allMembers = fullMembers,
|
||||
@@ -501,6 +507,19 @@ sealed class ConversationSettingsViewModel(
|
||||
override fun unblock() {
|
||||
repository.unblock(groupId)
|
||||
}
|
||||
|
||||
private fun loadMemberLabels(v2GroupId: GroupId.V2, groupMembers: List<GroupMemberEntry.FullMember>) = viewModelScope.launch(SignalDispatchers.IO) {
|
||||
val labelsByRecipientId = MemberLabelRepository.instance
|
||||
.getLabels(v2GroupId, groupMembers.map { it.member })
|
||||
|
||||
store.update { state ->
|
||||
state.copy(
|
||||
specificSettingsState = state.requireGroupSettingsState().copy(
|
||||
memberLabelsByRecipientId = labelsByRecipientId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.components.settings.conversation.preferences
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
@@ -10,6 +12,8 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.MemberLabelPillView
|
||||
import org.thoughtcrime.securesms.groups.memberlabel.StyledMemberLabel
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.ContextUtil
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
@@ -20,7 +24,7 @@ import org.thoughtcrime.securesms.util.visible
|
||||
import org.signal.core.ui.R as CoreUiR
|
||||
|
||||
/**
|
||||
* Renders a Recipient as a row item with an icon, avatar, status, and admin state
|
||||
* Renders a Recipient as a row item with an icon, avatar, label/status, and admin state.
|
||||
*/
|
||||
object RecipientPreference {
|
||||
|
||||
@@ -31,6 +35,7 @@ object RecipientPreference {
|
||||
class Model(
|
||||
val recipient: Recipient,
|
||||
val isAdmin: Boolean = false,
|
||||
val memberLabel: StyledMemberLabel? = null,
|
||||
val lifecycleOwner: LifecycleOwner? = null,
|
||||
val onClick: (() -> Unit)? = null
|
||||
) : PreferenceModel<Model>() {
|
||||
@@ -41,7 +46,8 @@ object RecipientPreference {
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) &&
|
||||
recipient.hasSameContent(newItem.recipient) &&
|
||||
isAdmin == newItem.isAdmin
|
||||
isAdmin == newItem.isAdmin &&
|
||||
memberLabel == newItem.memberLabel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,15 +55,14 @@ object RecipientPreference {
|
||||
private val avatar: AvatarImageView = itemView.findViewById(R.id.recipient_avatar)
|
||||
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 admin: View? = itemView.findViewById(R.id.admin)
|
||||
private val badge: BadgeImageView = itemView.findViewById(R.id.recipient_badge)
|
||||
|
||||
private var recipient: Recipient? = null
|
||||
|
||||
private val recipientObserver = object : Observer<Recipient> {
|
||||
override fun onChanged(recipient: Recipient) {
|
||||
onRecipientChanged(recipient)
|
||||
}
|
||||
private val recipientObserver = Observer<Recipient> { recipient ->
|
||||
onRecipientChanged(recipient)
|
||||
}
|
||||
|
||||
override fun bind(model: Model) {
|
||||
@@ -69,8 +74,9 @@ object RecipientPreference {
|
||||
|
||||
if (model.lifecycleOwner != null) {
|
||||
observeRecipient(model.lifecycleOwner, model.recipient)
|
||||
model.memberLabel?.let(::showMemberLabel)
|
||||
} else {
|
||||
onRecipientChanged(model.recipient)
|
||||
onRecipientChanged(model.recipient, model.memberLabel)
|
||||
}
|
||||
|
||||
admin?.visible = model.isAdmin
|
||||
@@ -80,7 +86,7 @@ object RecipientPreference {
|
||||
unbind()
|
||||
}
|
||||
|
||||
private fun onRecipientChanged(recipient: Recipient) {
|
||||
private fun onRecipientChanged(recipient: Recipient, memberLabel: StyledMemberLabel? = null) {
|
||||
avatar.setRecipient(recipient)
|
||||
badge.setBadgeFromRecipient(recipient)
|
||||
name.text = if (recipient.isSelf) {
|
||||
@@ -98,15 +104,36 @@ object RecipientPreference {
|
||||
}
|
||||
}
|
||||
|
||||
val aboutText = recipient.combinedAboutAndEmoji
|
||||
if (aboutText.isNullOrEmpty()) {
|
||||
about?.visibility = View.GONE
|
||||
} else {
|
||||
about?.text = recipient.combinedAboutAndEmoji
|
||||
about?.visibility = View.VISIBLE
|
||||
when {
|
||||
memberLabel != null -> showMemberLabel(memberLabel)
|
||||
|
||||
!recipient.combinedAboutAndEmoji.isNullOrEmpty() -> {
|
||||
about?.text = recipient.combinedAboutAndEmoji
|
||||
about?.visible = true
|
||||
memberLabelView?.visible = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
memberLabelView?.visible = false
|
||||
about?.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMemberLabel(styledLabel: StyledMemberLabel) {
|
||||
memberLabelView?.apply {
|
||||
style = MemberLabelPillView.Style(
|
||||
horizontalPadding = 8.dp,
|
||||
verticalPadding = 2.dp,
|
||||
textStyle = { MaterialTheme.typography.bodySmall }
|
||||
)
|
||||
setLabel(styledLabel.label, styledLabel.tintColor)
|
||||
visible = true
|
||||
}
|
||||
|
||||
about?.visible = false
|
||||
}
|
||||
|
||||
private fun observeRecipient(lifecycleOwner: LifecycleOwner?, recipient: Recipient?) {
|
||||
this.recipient?.live()?.liveData?.removeObserver(recipientObserver)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user