mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 18:30:20 +01:00
Block avatar downloads in message request states.
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
@@ -13,6 +17,7 @@ import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewKt;
|
||||
@@ -21,11 +26,16 @@ import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.signal.core.util.DimensionUnit;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarGradientColors;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AvatarDownloadStateCache;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.databinding.ConversationHeaderViewBinding;
|
||||
import org.thoughtcrime.securesms.fonts.SignalSymbols;
|
||||
import org.thoughtcrime.securesms.jobs.AvatarGroupsV2DownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ContextUtil;
|
||||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
@@ -34,8 +44,15 @@ import org.whispersystems.signalservice.api.util.Preconditions;
|
||||
|
||||
public class ConversationHeaderView extends ConstraintLayout {
|
||||
|
||||
private static final String TAG = Log.tag(ConversationHeaderView.class);
|
||||
private static final int FADE_DURATION = 150;
|
||||
private static final int LOADING_DELAY = 800;
|
||||
|
||||
private final ConversationHeaderViewBinding binding;
|
||||
|
||||
private boolean inProgress = false;
|
||||
private Handler handler = new Handler();
|
||||
|
||||
public ConversationHeaderView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -52,6 +69,30 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
binding = ConversationHeaderViewBinding.bind(this);
|
||||
}
|
||||
|
||||
public void showProgressBar(@NonNull Recipient recipient) {
|
||||
if (!inProgress) {
|
||||
inProgress = true;
|
||||
animateAvatarLoading(recipient);
|
||||
binding.messageRequestAvatarTapToView.setVisibility(GONE);
|
||||
binding.messageRequestAvatarTapToView.setOnClickListener(null);
|
||||
handler.postDelayed(() -> {
|
||||
boolean isDownloading = AvatarDownloadStateCache.getDownloadState(recipient) == AvatarDownloadStateCache.DownloadState.IN_PROGRESS;
|
||||
binding.progressBar.setVisibility(isDownloading ? View.VISIBLE : View.GONE);
|
||||
}, LOADING_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideProgressBar() {
|
||||
inProgress = false;
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void showFailedAvatarDownload(@NonNull Recipient recipient) {
|
||||
AvatarDownloadStateCache.set(recipient, AvatarDownloadStateCache.DownloadState.NONE);
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
binding.messageRequestAvatar.setImageDrawable(AvatarGradientColors.getGradientDrawable(recipient));
|
||||
}
|
||||
|
||||
public void setBadge(@Nullable Recipient recipient) {
|
||||
if (recipient == null || recipient.isSelf()) {
|
||||
binding.messageRequestBadge.setBadge(null);
|
||||
@@ -61,12 +102,25 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
}
|
||||
|
||||
public void setAvatar(@NonNull RequestManager requestManager, @Nullable Recipient recipient) {
|
||||
binding.messageRequestAvatar.setAvatar(requestManager, recipient, false);
|
||||
if (recipient == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipient != null && recipient.getShouldBlurAvatar() && recipient.getContactPhoto() != null) {
|
||||
if (AvatarDownloadStateCache.getDownloadState(recipient) != AvatarDownloadStateCache.DownloadState.IN_PROGRESS) {
|
||||
binding.messageRequestAvatar.setAvatar(requestManager, recipient, false, false, true);
|
||||
hideProgressBar();
|
||||
}
|
||||
|
||||
if (recipient.getShouldBlurAvatar() && recipient.getHasAvatar()) {
|
||||
binding.messageRequestAvatarTapToView.setVisibility(VISIBLE);
|
||||
binding.messageRequestAvatarTapToView.setOnClickListener(v -> {
|
||||
SignalExecutors.BOUNDED.execute(() -> SignalDatabase.recipients().manuallyShowAvatar(recipient.getId()));
|
||||
AvatarDownloadStateCache.set(recipient, AvatarDownloadStateCache.DownloadState.IN_PROGRESS);
|
||||
SignalExecutors.BOUNDED.execute(() -> SignalDatabase.recipients().manuallyUpdateShowAvatar(recipient.getId(), true));
|
||||
if (recipient.isPushV2Group()) {
|
||||
AvatarGroupsV2DownloadJob.enqueueUnblurredAvatar(recipient.requireGroupId().requireV2());
|
||||
} else {
|
||||
RetrieveProfileAvatarJob.enqueueUnblurredAvatar(recipient);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
binding.messageRequestAvatarTapToView.setVisibility(GONE);
|
||||
@@ -209,6 +263,22 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
binding.messageRequestDescription.setMovementMethod(enable ? LongClickMovementMethod.getInstance(getContext()) : null);
|
||||
}
|
||||
|
||||
private void animateAvatarLoading(@NonNull Recipient recipient) {
|
||||
Drawable loadingProfile = AppCompatResources.getDrawable(getContext(), R.drawable.circle_profile_photo);
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(binding.messageRequestAvatar, "alpha", 1f, 0f).setDuration(FADE_DURATION);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (AvatarDownloadStateCache.getDownloadState(recipient) == AvatarDownloadStateCache.DownloadState.IN_PROGRESS) {
|
||||
binding.messageRequestAvatar.setImageDrawable(loadingProfile);
|
||||
}
|
||||
ObjectAnimator.ofFloat(binding.messageRequestAvatar, "alpha", 0f, 1f).setDuration(FADE_DURATION).start();
|
||||
}
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void updateOutlineVisibility() {
|
||||
if (ViewKt.isVisible(binding.messageRequestSubtitle) || ViewKt.isVisible(binding.messageRequestDescription)) {
|
||||
if (getBackground() != null) {
|
||||
|
||||
@@ -142,7 +142,7 @@ public class ConversationTitleView extends ConstraintLayout {
|
||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(startDrawable, null, endDrawable, null);
|
||||
|
||||
if (recipient != null) {
|
||||
this.avatar.displayChatAvatar(requestManager, recipient, false);
|
||||
this.avatar.displayChatAvatar(requestManager, recipient, false, true);
|
||||
}
|
||||
|
||||
if (recipient == null || recipient.isSelf()) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.thoughtcrime.securesms.conversation.colors
|
||||
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import androidx.annotation.ColorInt
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Lists gradients used to hide profiles during message request states
|
||||
*/
|
||||
object AvatarGradientColors {
|
||||
|
||||
@JvmStatic
|
||||
fun getGradientDrawable(recipient: Recipient): GradientDrawable {
|
||||
return if (recipient.serviceId.getOrNull() != null) {
|
||||
gradients[abs(recipient.requireServiceId().hashCode() % gradients.size)].getDrawable()
|
||||
} else if (recipient.groupId.getOrNull() != null) {
|
||||
gradients[abs(recipient.requireGroupId().hashCode() % gradients.size)].getDrawable()
|
||||
} else {
|
||||
gradients[0].getDrawable()
|
||||
}
|
||||
}
|
||||
|
||||
private val gradients = listOf(
|
||||
AvatarGradientColor(0xFF252568.toInt(), 0xFF9C8F8F.toInt()),
|
||||
AvatarGradientColor(0xFF2A4275.toInt(), 0xFF9D9EA1.toInt()),
|
||||
AvatarGradientColor(0xFF2E4B5F.toInt(), 0xFF8AA9B1.toInt()),
|
||||
AvatarGradientColor(0xFF2E426C.toInt(), 0xFF7A9377.toInt()),
|
||||
AvatarGradientColor(0xFF1A341A.toInt(), 0xFF807F6E.toInt()),
|
||||
AvatarGradientColor(0xFF464E42.toInt(), 0xFFD5C38F.toInt()),
|
||||
AvatarGradientColor(0xFF595643.toInt(), 0xFF93A899.toInt()),
|
||||
AvatarGradientColor(0xFF2C2F36.toInt(), 0xFF687466.toInt()),
|
||||
AvatarGradientColor(0xFF2B1E18.toInt(), 0xFF968980.toInt()),
|
||||
AvatarGradientColor(0xFF7B7067.toInt(), 0xFFA5A893.toInt()),
|
||||
AvatarGradientColor(0xFF706359.toInt(), 0xFFBDA194.toInt()),
|
||||
AvatarGradientColor(0xFF383331.toInt(), 0xFFA48788.toInt()),
|
||||
AvatarGradientColor(0xFF924F4F.toInt(), 0xFF897A7A.toInt()),
|
||||
AvatarGradientColor(0xFF663434.toInt(), 0xFFC58D77.toInt()),
|
||||
AvatarGradientColor(0xFF8F4B02.toInt(), 0xFFAA9274.toInt()),
|
||||
AvatarGradientColor(0xFF784747.toInt(), 0xFF8C8F6F.toInt()),
|
||||
AvatarGradientColor(0xFF747474.toInt(), 0xFFACACAC.toInt()),
|
||||
AvatarGradientColor(0xFF49484C.toInt(), 0xFFA5A6B5.toInt()),
|
||||
AvatarGradientColor(0xFF4A4E4D.toInt(), 0xFFABAFAE.toInt()),
|
||||
AvatarGradientColor(0xFF3A3A3A.toInt(), 0xFF929887.toInt())
|
||||
)
|
||||
|
||||
data class AvatarGradientColor(@ColorInt val startGradient: Int, @ColorInt val endGradient: Int) {
|
||||
fun getDrawable(): GradientDrawable {
|
||||
val gradientDrawable = GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(startGradient, endGradient)
|
||||
)
|
||||
gradientDrawable.shape = GradientDrawable.OVAL
|
||||
|
||||
return gradientDrawable
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.conversation.colors.Colorizable
|
||||
import org.thoughtcrime.securesms.conversation.colors.Colorizer
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.Multiselectable
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AvatarDownloadStateCache
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationMessageElement
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationUpdate
|
||||
@@ -536,7 +537,19 @@ class ConversationAdapterV2(
|
||||
val (recipient, groupInfo, sharedGroups, messageRequestState) = model.recipientInfo
|
||||
val isSelf = recipient.id == Recipient.self().id
|
||||
|
||||
conversationBanner.setAvatar(requestManager, recipient)
|
||||
when (model.avatarDownloadState) {
|
||||
AvatarDownloadStateCache.DownloadState.NONE,
|
||||
AvatarDownloadStateCache.DownloadState.FINISHED -> {
|
||||
conversationBanner.setAvatar(requestManager, recipient)
|
||||
}
|
||||
AvatarDownloadStateCache.DownloadState.IN_PROGRESS -> {
|
||||
conversationBanner.showProgressBar(recipient)
|
||||
}
|
||||
AvatarDownloadStateCache.DownloadState.FAILED -> {
|
||||
conversationBanner.showFailedAvatarDownload(recipient)
|
||||
}
|
||||
}
|
||||
|
||||
conversationBanner.showBackgroundBubble(recipient.hasWallpaper)
|
||||
val title: String = conversationBanner.setTitle(recipient) {
|
||||
displayDialogFragment(AboutSheet.create(recipient))
|
||||
|
||||
@@ -67,6 +67,7 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.ConversationLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -202,6 +203,7 @@ import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryReplace
|
||||
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryResultsControllerV2
|
||||
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryViewModelV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.computed.ConversationMessageComputeWorkers
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AvatarDownloadStateCache
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationMessageElement
|
||||
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupCallViewModel
|
||||
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupViewModel
|
||||
@@ -1056,6 +1058,28 @@ class ConversationFragment :
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
val recipient = viewModel.recipientSnapshot
|
||||
if (recipient != null) {
|
||||
AvatarDownloadStateCache.forRecipient(recipient.id).collect {
|
||||
when (it) {
|
||||
AvatarDownloadStateCache.DownloadState.NONE,
|
||||
AvatarDownloadStateCache.DownloadState.IN_PROGRESS,
|
||||
AvatarDownloadStateCache.DownloadState.FINISHED -> {
|
||||
viewModel.updateThreadHeader()
|
||||
}
|
||||
AvatarDownloadStateCache.DownloadState.FAILED -> {
|
||||
Snackbar.make(requireView(), R.string.ConversationFragment_photo_failed, Snackbar.LENGTH_LONG).show()
|
||||
presentConversationTitle(recipient)
|
||||
viewModel.onAvatarDownloadFailed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TextSecurePreferences.getServiceOutage(context)) {
|
||||
AppDependencies.jobManager.add(ServiceOutageDetectionJob())
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bumptech.glide.RequestManager
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
@@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.rx3.asFlow
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.paging.ProxyPagingController
|
||||
@@ -55,6 +57,7 @@ import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
|
||||
import org.thoughtcrime.securesms.conversation.v2.items.ChatColorsDrawable
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.MessageTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.Mention
|
||||
@@ -310,6 +313,20 @@ class ConversationViewModel(
|
||||
})
|
||||
}
|
||||
|
||||
fun onAvatarDownloadFailed() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val recipient = recipientSnapshot
|
||||
if (recipient != null) {
|
||||
recipients.manuallyUpdateShowAvatar(recipient.id, false)
|
||||
}
|
||||
pagingController.onDataItemChanged(ConversationElementKey.threadHeader)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateThreadHeader() {
|
||||
pagingController.onDataItemChanged(ConversationElementKey.threadHeader)
|
||||
}
|
||||
|
||||
fun getBannerFlows(
|
||||
context: Context,
|
||||
groupJoinClickListener: () -> Unit,
|
||||
|
||||
@@ -4,9 +4,14 @@ import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.signal.core.util.Result
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason
|
||||
import org.thoughtcrime.securesms.jobs.AvatarGroupsV2DownloadJob
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
/**
|
||||
@@ -29,8 +34,24 @@ class MessageRequestViewModel(
|
||||
fun onAccept(): Single<Result<Unit, GroupChangeFailureReason>> {
|
||||
return recipientId
|
||||
.flatMap { recipientId ->
|
||||
val recipient = Recipient.resolved(recipientId)
|
||||
if (recipient.isPushV2Group) {
|
||||
if (recipient.shouldBlurAvatar && recipient.hasAvatar) {
|
||||
AvatarGroupsV2DownloadJob.enqueueUnblurredAvatar(recipient.requireGroupId().requireV2())
|
||||
}
|
||||
|
||||
val jobs = recipient.participantIds
|
||||
.map { Recipient.resolved(it) }
|
||||
.filter { it.shouldBlurAvatar && it.hasAvatar }
|
||||
.map { RetrieveProfileAvatarJob(it, it.profileAvatar, true, true) }
|
||||
AppDependencies.jobManager.addAll(jobs)
|
||||
} else if (recipient.shouldBlurAvatar && recipient.hasAvatar) {
|
||||
RetrieveProfileAvatarJob.enqueueUnblurredAvatar(recipient)
|
||||
}
|
||||
|
||||
messageRequestRepository.acceptMessageRequest(recipientId, threadId)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
/**
|
||||
* Cache used to store the progress of both 1:1 and group avatar downloads
|
||||
*/
|
||||
object AvatarDownloadStateCache {
|
||||
|
||||
private val TAG = Log.tag(AvatarDownloadStateCache::class.java)
|
||||
|
||||
private val cache = HashMap<RecipientId, MutableStateFlow<DownloadState>>(100)
|
||||
|
||||
@JvmStatic
|
||||
fun set(recipient: Recipient, downloadState: DownloadState) {
|
||||
if (cache[recipient.id] == null) {
|
||||
cache[recipient.id] = MutableStateFlow(downloadState)
|
||||
} else {
|
||||
cache[recipient.id]!!.update { downloadState }
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDownloadState(recipient: Recipient): DownloadState {
|
||||
return cache[recipient.id]?.value ?: DownloadState.NONE
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun forRecipient(id: RecipientId): StateFlow<DownloadState> {
|
||||
if (cache[id] == null) {
|
||||
cache[id] = MutableStateFlow(DownloadState.NONE)
|
||||
}
|
||||
|
||||
return cache[id]!!.asStateFlow()
|
||||
}
|
||||
|
||||
enum class DownloadState {
|
||||
NONE,
|
||||
IN_PROGRESS,
|
||||
FINISHED,
|
||||
FAILED
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ class ConversationDataSource(
|
||||
}
|
||||
|
||||
private fun loadThreadHeader(): ThreadHeader {
|
||||
return ThreadHeader(messageRequestRepository.getRecipientInfo(threadRecipient.id, threadId))
|
||||
return ThreadHeader(messageRequestRepository.getRecipientInfo(threadRecipient.id, threadId), AvatarDownloadStateCache.getDownloadState(threadRecipient))
|
||||
}
|
||||
|
||||
private fun ConversationMessage.toMappingModel(): MappingModel<*> {
|
||||
|
||||
@@ -73,7 +73,7 @@ data class IncomingMedia(
|
||||
}
|
||||
}
|
||||
|
||||
data class ThreadHeader(val recipientInfo: MessageRequestRecipientInfo) : MappingModel<ThreadHeader> {
|
||||
data class ThreadHeader(val recipientInfo: MessageRequestRecipientInfo, val avatarDownloadState: AvatarDownloadStateCache.DownloadState) : MappingModel<ThreadHeader> {
|
||||
override fun areItemsTheSame(newItem: ThreadHeader): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user