mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 00:29:11 +01:00
Refactor Message Request logic to fix some GV1->GV2 bugs.
This commit is contained in:
committed by
Alex Hart
parent
ce44e3949c
commit
43e3ef2bee
@@ -72,44 +72,57 @@ final class MessageRequestRepository {
|
||||
});
|
||||
}
|
||||
|
||||
void getMessageRequestState(@NonNull Recipient recipient, long threadId, @NonNull Consumer<MessageRequestState> state) {
|
||||
executor.execute(() -> state.accept(findMessageRequestState(recipient, threadId)));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private MessageRequestState findMessageRequestState(@NonNull Recipient recipient, long threadId) {
|
||||
if (recipient.isGroup() && recipient.isPushV2Group()) {
|
||||
GroupDatabase.MemberLevel memberLevel = DatabaseFactory.getGroupDatabase(context)
|
||||
.getGroup(recipient.getId())
|
||||
.transform(g -> g.memberLevel(Recipient.self()))
|
||||
.or(GroupDatabase.MemberLevel.NOT_A_MEMBER);
|
||||
|
||||
if (memberLevel == GroupDatabase.MemberLevel.PENDING_MEMBER) {
|
||||
return MessageRequestState.REQUIRED;
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient.isPushV1Group() && FeatureFlags.groupsV1ForcedMigration()) {
|
||||
return MessageRequestState.REQUIRED;
|
||||
}
|
||||
|
||||
if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
@NonNull MessageRequestState getMessageRequestState(@NonNull Recipient recipient, long threadId) {
|
||||
if (recipient.isBlocked()) {
|
||||
if (recipient.isGroup()) {
|
||||
GroupDatabase.MemberLevel memberLevel = DatabaseFactory.getGroupDatabase(context)
|
||||
.getGroup(recipient.getId())
|
||||
.transform(g -> g.memberLevel(Recipient.self()))
|
||||
.or(GroupDatabase.MemberLevel.NOT_A_MEMBER);
|
||||
|
||||
if (memberLevel == GroupDatabase.MemberLevel.NOT_A_MEMBER) {
|
||||
return MessageRequestState.NOT_REQUIRED;
|
||||
}
|
||||
return MessageRequestState.BLOCKED_GROUP;
|
||||
} else {
|
||||
return MessageRequestState.BLOCKED_INDIVIDUAL;
|
||||
}
|
||||
} else if (threadId <= 0) {
|
||||
return MessageRequestState.NONE;
|
||||
} else if (recipient.isPushV2Group()) {
|
||||
switch (getGroupMemberLevel(recipient.getId())) {
|
||||
case NOT_A_MEMBER:
|
||||
return MessageRequestState.NONE;
|
||||
case PENDING_MEMBER:
|
||||
return MessageRequestState.GROUP_V2_INVITE;
|
||||
default:
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
return MessageRequestState.GROUP_V2_ADD;
|
||||
}
|
||||
}
|
||||
} else if (!RecipientUtil.isLegacyProfileSharingAccepted(recipient) && isLegacyThread(recipient)) {
|
||||
if (recipient.isGroup()) {
|
||||
return MessageRequestState.LEGACY_GROUP_V1;
|
||||
} else {
|
||||
return MessageRequestState.LEGACY_INDIVIDUAL;
|
||||
}
|
||||
} else if (recipient.isPushV1Group()) {
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
if (FeatureFlags.groupsV1ForcedMigration()) {
|
||||
if (recipient.getParticipants().size() > FeatureFlags.groupLimits().getHardLimit()) {
|
||||
return MessageRequestState.DEPRECATED_GROUP_V1_TOO_LARGE;
|
||||
} else {
|
||||
return MessageRequestState.DEPRECATED_GROUP_V1;
|
||||
}
|
||||
} else {
|
||||
return MessageRequestState.NONE;
|
||||
}
|
||||
} else if (!recipient.isActiveGroup()) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
return MessageRequestState.GROUP_V1;
|
||||
}
|
||||
|
||||
return MessageRequestState.REQUIRED;
|
||||
} else if (!RecipientUtil.isLegacyProfileSharingAccepted(recipient) && threadId > 0) {
|
||||
return MessageRequestState.REQUIRED;
|
||||
} else {
|
||||
return MessageRequestState.NOT_REQUIRED;
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
return MessageRequestState.INDIVIDUAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,23 +272,20 @@ final class MessageRequestRepository {
|
||||
});
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
boolean isPendingMember(@NonNull GroupId.V2 groupId) {
|
||||
return DatabaseFactory.getGroupDatabase(context).isPendingMember(groupId, Recipient.self());
|
||||
private GroupDatabase.MemberLevel getGroupMemberLevel(@NonNull RecipientId recipientId) {
|
||||
return DatabaseFactory.getGroupDatabase(context)
|
||||
.getGroup(recipientId)
|
||||
.transform(g -> g.memberLevel(Recipient.self()))
|
||||
.or(GroupDatabase.MemberLevel.NOT_A_MEMBER);
|
||||
}
|
||||
|
||||
enum MessageRequestState {
|
||||
/**
|
||||
* Message request permission does not need to be gained at this time.
|
||||
* <p>
|
||||
* Either:
|
||||
* - Explicit message request has been accepted, or;
|
||||
* - Did not need to be shown because they are a contact etc, or;
|
||||
* - It's a group that they are no longer in or invited to.
|
||||
*/
|
||||
NOT_REQUIRED,
|
||||
|
||||
/** Explicit message request permission is required. */
|
||||
REQUIRED
|
||||
@WorkerThread
|
||||
private boolean isLegacyThread(@NonNull Recipient recipient) {
|
||||
Context context = ApplicationDependencies.getApplication();
|
||||
Long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient.getId());
|
||||
|
||||
return threadId != null &&
|
||||
(RecipientUtil.hasSentMessageInThread(context, threadId) || RecipientUtil.isPreMessageRequestThread(context, threadId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.thoughtcrime.securesms.messagerequests;
|
||||
|
||||
/**
|
||||
* An enum representing the possible message request states a user can be in.
|
||||
*/
|
||||
public enum MessageRequestState {
|
||||
/** No message request necessary */
|
||||
NONE,
|
||||
|
||||
/** A user is blocked */
|
||||
BLOCKED_INDIVIDUAL,
|
||||
|
||||
/** A group is blocked */
|
||||
BLOCKED_GROUP,
|
||||
|
||||
/** An individual conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_INDIVIDUAL,
|
||||
|
||||
/** A V1 group conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_GROUP_V1,
|
||||
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on. */
|
||||
DEPRECATED_GROUP_V1,
|
||||
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on, but it's also too large to migrate. Nothing we can do. */
|
||||
DEPRECATED_GROUP_V1_TOO_LARGE,
|
||||
|
||||
/** A message request is needed for a V1 group */
|
||||
GROUP_V1,
|
||||
|
||||
/** An invite response is needed for a V2 group */
|
||||
GROUP_V2_INVITE,
|
||||
|
||||
/** A message request is needed for a V2 group */
|
||||
GROUP_V2_ADD,
|
||||
|
||||
/** A message request is needed for an individual */
|
||||
INDIVIDUAL
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataTriple;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -39,7 +38,6 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
private final LiveData<MessageData> messageData;
|
||||
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
|
||||
private final MutableLiveData<GroupMemberCount> memberCount = new MutableLiveData<>(GroupMemberCount.ZERO);
|
||||
private final MutableLiveData<DisplayState> displayState = new MutableLiveData<>();
|
||||
private final LiveData<RequestReviewDisplayState> requestReviewDisplayState;
|
||||
private final LiveData<RecipientInfo> recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups),
|
||||
triple -> new RecipientInfo(triple.first(), triple.second(), triple.third()));
|
||||
@@ -50,7 +48,6 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
private long threadId;
|
||||
|
||||
private final RecipientForeverObserver recipientObserver = recipient -> {
|
||||
loadMessageRequestAccepted(recipient);
|
||||
loadMemberCount();
|
||||
this.recipient.setValue(recipient);
|
||||
};
|
||||
@@ -58,8 +55,7 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
private MessageRequestViewModel(MessageRequestRepository repository) {
|
||||
this.repository = repository;
|
||||
this.messageData = LiveDataUtil.mapAsync(recipient, this::createMessageDataForRecipient);
|
||||
this.requestReviewDisplayState = LiveDataUtil.mapAsync(LiveDataUtil.combineLatest(messageData, displayState, MessageDataDisplayStateHolder::new),
|
||||
MessageRequestViewModel::transformHolderToReviewDisplayState);
|
||||
this.requestReviewDisplayState = LiveDataUtil.mapAsync(messageData, MessageRequestViewModel::transformHolderToReviewDisplayState);
|
||||
}
|
||||
|
||||
public void setConversationInfo(@NonNull RecipientId recipientId, long threadId) {
|
||||
@@ -82,10 +78,6 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<DisplayState> getMessageRequestDisplayState() {
|
||||
return displayState;
|
||||
}
|
||||
|
||||
public LiveData<RequestReviewDisplayState> getRequestReviewDisplayState() {
|
||||
return requestReviewDisplayState;
|
||||
}
|
||||
@@ -111,7 +103,8 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public boolean shouldShowMessageRequest() {
|
||||
return displayState.getValue() == DisplayState.DISPLAY_MESSAGE_REQUEST;
|
||||
MessageData data = messageData.getValue();
|
||||
return data != null && data.getMessageState() != MessageRequestState.NONE;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@@ -173,11 +166,10 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
repository.getMemberCount(liveRecipient.getId(), memberCount::postValue);
|
||||
}
|
||||
|
||||
private static RequestReviewDisplayState transformHolderToReviewDisplayState(@NonNull MessageDataDisplayStateHolder holder) {
|
||||
if (holder.messageData.messageClass == MessageClass.INDIVIDUAL && holder.displayState == DisplayState.DISPLAY_MESSAGE_REQUEST) {
|
||||
return ReviewUtil.isRecipientReviewSuggested(holder.messageData.getRecipient().getId())
|
||||
? RequestReviewDisplayState.SHOWN
|
||||
: RequestReviewDisplayState.HIDDEN;
|
||||
private static RequestReviewDisplayState transformHolderToReviewDisplayState(@NonNull MessageData holder) {
|
||||
if (holder.getMessageState() == MessageRequestState.INDIVIDUAL) {
|
||||
return ReviewUtil.isRecipientReviewSuggested(holder.getRecipient().getId()) ? RequestReviewDisplayState.SHOWN
|
||||
: RequestReviewDisplayState.HIDDEN;
|
||||
} else {
|
||||
return RequestReviewDisplayState.NONE;
|
||||
}
|
||||
@@ -185,63 +177,8 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
|
||||
@WorkerThread
|
||||
private @NonNull MessageData createMessageDataForRecipient(@NonNull Recipient recipient) {
|
||||
if (recipient.isBlocked()) {
|
||||
if (recipient.isGroup()) {
|
||||
return new MessageData(recipient, MessageClass.BLOCKED_GROUP);
|
||||
} else {
|
||||
return new MessageData(recipient, MessageClass.BLOCKED_INDIVIDUAL);
|
||||
}
|
||||
} else if (recipient.isPushV2Group()) {
|
||||
if (repository.isPendingMember(recipient.requireGroupId().requireV2())) {
|
||||
return new MessageData(recipient, MessageClass.GROUP_V2_INVITE);
|
||||
} else {
|
||||
return new MessageData(recipient, MessageClass.GROUP_V2_ADD);
|
||||
}
|
||||
} else if (recipient.isPushV1Group() && FeatureFlags.groupsV1ForcedMigration()) {
|
||||
if (recipient.getParticipants().size() > FeatureFlags.groupLimits().getHardLimit()) {
|
||||
return new MessageData(recipient, MessageClass.DEPRECATED_GROUP_V1_TOO_LARGE);
|
||||
} else {
|
||||
return new MessageData(recipient, MessageClass.DEPRECATED_GROUP_V1);
|
||||
}
|
||||
} else if (isLegacyThread(recipient)) {
|
||||
if (recipient.isGroup()) {
|
||||
return new MessageData(recipient, MessageClass.LEGACY_GROUP_V1);
|
||||
} else {
|
||||
return new MessageData(recipient, MessageClass.LEGACY_INDIVIDUAL);
|
||||
}
|
||||
} else if (recipient.isGroup()) {
|
||||
return new MessageData(recipient, MessageClass.GROUP_V1);
|
||||
} else {
|
||||
return new MessageData(recipient, MessageClass.INDIVIDUAL);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void loadMessageRequestAccepted(@NonNull Recipient recipient) {
|
||||
if (recipient.isBlocked()) {
|
||||
displayState.postValue(DisplayState.DISPLAY_MESSAGE_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
repository.getMessageRequestState(recipient, threadId, accepted -> {
|
||||
switch (accepted) {
|
||||
case NOT_REQUIRED:
|
||||
displayState.postValue(DisplayState.DISPLAY_NONE);
|
||||
break;
|
||||
case REQUIRED:
|
||||
displayState.postValue(DisplayState.DISPLAY_MESSAGE_REQUEST);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private boolean isLegacyThread(@NonNull Recipient recipient) {
|
||||
Context context = ApplicationDependencies.getApplication();
|
||||
Long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient.getId());
|
||||
|
||||
return threadId != null &&
|
||||
(RecipientUtil.hasSentMessageInThread(context, threadId) || RecipientUtil.isPreMessageRequestThread(context, threadId));
|
||||
MessageRequestState state = repository.getMessageRequestState(recipient, threadId);
|
||||
return new MessageData(recipient, state);
|
||||
}
|
||||
|
||||
public static class RecipientInfo {
|
||||
@@ -284,27 +221,6 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
ACCEPTED
|
||||
}
|
||||
|
||||
public enum DisplayState {
|
||||
DISPLAY_MESSAGE_REQUEST, DISPLAY_NONE
|
||||
}
|
||||
|
||||
public enum MessageClass {
|
||||
BLOCKED_INDIVIDUAL,
|
||||
BLOCKED_GROUP,
|
||||
/** An individual conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_INDIVIDUAL,
|
||||
/** A V1 group conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_GROUP_V1,
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on. */
|
||||
DEPRECATED_GROUP_V1,
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on, but it's also too large to migrate. Nothing we can do. */
|
||||
DEPRECATED_GROUP_V1_TOO_LARGE,
|
||||
GROUP_V1,
|
||||
GROUP_V2_INVITE,
|
||||
GROUP_V2_ADD,
|
||||
INDIVIDUAL
|
||||
}
|
||||
|
||||
public enum RequestReviewDisplayState {
|
||||
HIDDEN,
|
||||
SHOWN,
|
||||
@@ -313,29 +229,19 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
|
||||
public static final class MessageData {
|
||||
private final Recipient recipient;
|
||||
private final MessageClass messageClass;
|
||||
private final MessageRequestState messageState;
|
||||
|
||||
public MessageData(@NonNull Recipient recipient, @NonNull MessageClass messageClass) {
|
||||
public MessageData(@NonNull Recipient recipient, @NonNull MessageRequestState messageState) {
|
||||
this.recipient = recipient;
|
||||
this.messageClass = messageClass;
|
||||
this.messageState = messageState;
|
||||
}
|
||||
|
||||
public @NonNull Recipient getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public @NonNull MessageClass getMessageClass() {
|
||||
return messageClass;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MessageDataDisplayStateHolder {
|
||||
private final MessageData messageData;
|
||||
private final DisplayState displayState;
|
||||
|
||||
private MessageDataDisplayStateHolder(@NonNull MessageData messageData, @NonNull DisplayState displayState) {
|
||||
this.messageData = messageData;
|
||||
this.displayState = displayState;
|
||||
public @NonNull MessageRequestState getMessageState() {
|
||||
return messageState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
@@ -74,7 +73,7 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
question.setLearnMoreVisible(false);
|
||||
question.setOnLinkClickListener(null);
|
||||
|
||||
switch (messageData.getMessageClass()) {
|
||||
switch (messageData.getMessageState()) {
|
||||
case BLOCKED_INDIVIDUAL:
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(getContext()))), 0));
|
||||
|
||||
Reference in New Issue
Block a user