From 066892c11ab041b184fa9b81fe6594440e33547c Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 2 Jul 2024 15:50:35 -0300 Subject: [PATCH] Make group subtitles auto-update as names change. --- .../conversation/ConversationTitleView.java | 16 ++++--------- .../conversation/v2/ConversationFragment.kt | 23 +++++++++++++++++++ .../conversation/v2/ConversationViewModel.kt | 20 ++++++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java index 233629c16d..e04a2a9698 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java @@ -15,8 +15,6 @@ import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; import com.bumptech.glide.RequestManager; import org.thoughtcrime.securesms.R; @@ -176,6 +174,11 @@ public class ConversationTitleView extends ConstraintLayout { updateVerifiedSubtitleVisibility(); } + public void setGroupRecipientSubtitle(@Nullable String members) { + this.subtitle.setText(members); + updateSubtitleVisibility(); + } + private void setComposeTitle() { this.title.setText(R.string.ConversationActivity_compose_message); this.subtitle.setText(null); @@ -190,15 +193,6 @@ public class ConversationTitleView extends ConstraintLayout { private void setGroupRecipientTitle(@NonNull Recipient recipient) { this.title.setText(recipient.getDisplayName(getContext())); - this.subtitle.setText(Stream.of(recipient.getParticipantIds()) - .limit(10) - .map(Recipient::resolved) - .sorted((a, b) -> Boolean.compare(a.isSelf(), b.isSelf())) - .map(r -> r.isSelf() ? getResources().getString(R.string.ConversationTitleView_you) - : r.getDisplayName(getContext())) - .collect(Collectors.joining(", "))); - - updateSubtitleVisibility(); } private void setSelfTitle() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 5754d0a954..73f63b77f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -581,6 +581,7 @@ class ConversationFragment : presentWallpaper(args.wallpaper) presentChatColors(args.chatColors) presentConversationTitle(viewModel.recipientSnapshot) + presentGroupConversationSubtitle(createGroupSubtitleString(viewModel.titleViewParticipantsSnapshot)) presentActionBarMenu() presentStoryRing() @@ -865,6 +866,10 @@ class ConversationFragment : //endregion + private fun createGroupSubtitleString(members: List): String { + return members.joinToString(", ") { r -> if (r.isSelf) getString(R.string.ConversationTitleView_you) else r.getDisplayName(requireContext()) } + } + private fun observeConversationThread() { var firstRender = true disposables += viewModel @@ -924,6 +929,12 @@ class ConversationFragment : .distinctUntilChanged { r1, r2 -> r1 === r2 || r1.hasSameContent(r2) } .subscribeBy(onNext = this::onRecipientChanged) + disposables += viewModel.titleViewParticipants + .map { createGroupSubtitleString(it) } + .distinctUntilChanged() + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy(onNext = this::presentGroupConversationSubtitle) + disposables += viewModel.scrollButtonState .subscribeBy(onNext = this::presentScrollButtons) @@ -1315,6 +1326,17 @@ class ConversationFragment : } } + private fun presentGroupConversationSubtitle(subtitle: String) { + val titleView = binding.conversationTitleView.root + + if (subtitle.isBlank()) { + titleView.setGroupRecipientSubtitle(null) + return + } + + titleView.setGroupRecipientSubtitle(subtitle) + } + private fun presentConversationTitle(recipient: Recipient?) { if (recipient == null) { return @@ -1323,6 +1345,7 @@ class ConversationFragment : val titleView = binding.conversationTitleView.root titleView.setTitle(Glide.with(this), recipient) + if (recipient.expiresInSeconds > 0) { titleView.showExpiring(recipient) } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index b886c49127..416871a6b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -101,6 +101,17 @@ class ConversationViewModel( get() = scrollButtonStateStore.state.unreadCount val recipient: Observable = recipientRepository.conversationRecipient + val titleViewParticipants: Observable> = recipient.filter { it.isGroup }.switchMap { groupRecipient -> + val firstTenIds = groupRecipient.participantIds + .take(10) + .sortedBy { it == Recipient.self().id } + + Observable.combineLatest( + firstTenIds.map { Recipient.observable(it) } + ) { objects -> + objects.toList() as List + } + } private val _conversationThreadState: Subject = BehaviorSubject.create() val conversationThreadState: Single = _conversationThreadState.firstOrError() @@ -122,6 +133,10 @@ class ConversationViewModel( var recipientSnapshot: Recipient? = null private set + @Volatile + var titleViewParticipantsSnapshot: List = emptyList() + private set + val isPushAvailable: Boolean get() = recipientSnapshot?.isRegistered == true && Recipient.self().isRegistered @@ -167,6 +182,11 @@ class ConversationViewModel( recipientSnapshot = it } + disposables += titleViewParticipants + .subscribeBy { + titleViewParticipantsSnapshot = it + } + val chatColorsDataObservable: Observable = Observable.combineLatest( recipient.map { it.chatColors }.distinctUntilChanged(), chatBounds.distinctUntilChanged()