Join group via invite link.

This commit is contained in:
Alan Evans
2020-08-26 12:51:25 -03:00
committed by GitHub
parent b58376920f
commit 860f06ec9e
45 changed files with 2488 additions and 271 deletions

View File

@@ -227,6 +227,7 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.stickers.StickerManagementActivity;
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent;
import org.thoughtcrime.securesms.stickers.StickerSearchRepository;
import org.thoughtcrime.securesms.util.AsynchronousCallback;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
@@ -356,6 +357,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
private InputPanel inputPanel;
private View panelParent;
private View noLongerMemberBanner;
private View requestingMemberBanner;
private View cancelJoinRequest;
private Stub<View> mentionsSuggestions;
private LinkPreviewViewModel linkPreviewViewModel;
@@ -383,12 +386,21 @@ public class ConversationActivity extends PassphraseRequiredActivity
long threadId,
int distributionType,
int startingPosition)
{
Intent intent = buildIntent(context, recipientId, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition);
return intent;
}
public static @NonNull Intent buildIntent(@NonNull Context context,
@NonNull RecipientId recipientId,
long threadId)
{
Intent intent = new Intent(context, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipientId);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition);
return intent;
}
@@ -1452,14 +1464,64 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
private void initializeEnabledCheck() {
groupViewModel.getGroupActiveState().observe(this, state -> {
boolean inactivePushGroup = state != null && isPushGroupConversation() && !state.isActiveGroup();
boolean enabled = !inactivePushGroup;
noLongerMemberBanner.setVisibility(enabled ? View.GONE : View.VISIBLE);
inputPanel.setVisibility(enabled ? View.VISIBLE : View.GONE);
inputPanel.setEnabled(enabled);
sendButton.setEnabled(enabled);
attachButton.setEnabled(enabled);
groupViewModel.getSelfMemberLevel().observe(this, selfMemberShip -> {
boolean canSendMessages;
boolean leftGroup;
boolean canCancelRequest;
if (selfMemberShip == null) {
leftGroup = false;
canSendMessages = true;
canCancelRequest = false;
} else {
switch (selfMemberShip) {
case NOT_A_MEMBER:
leftGroup = true;
canSendMessages = false;
canCancelRequest = false;
break;
case PENDING_MEMBER:
leftGroup = false;
canSendMessages = false;
canCancelRequest = false;
break;
case REQUESTING_MEMBER:
leftGroup = false;
canSendMessages = false;
canCancelRequest = true;
break;
case FULL_MEMBER:
case ADMINISTRATOR:
leftGroup = false;
canSendMessages = true;
canCancelRequest = false;
break;
default:
throw new AssertionError();
}
}
noLongerMemberBanner.setVisibility(leftGroup ? View.VISIBLE : View.GONE);
requestingMemberBanner.setVisibility(canCancelRequest ? View.VISIBLE : View.GONE);
if (canCancelRequest) {
cancelJoinRequest.setOnClickListener(v -> ConversationGroupViewModel.onCancelJoinRequest(getRecipient(), new AsynchronousCallback.MainThread<Void, GroupChangeFailureReason>() {
@Override
public void onComplete(@Nullable Void result) {
Log.d(TAG, "Cancel request complete");
}
@Override
public void onError(@Nullable GroupChangeFailureReason error) {
Log.d(TAG, "Cancel join request failed " + error);
Toast.makeText(ConversationActivity.this, GroupErrors.getUserDisplayMessage(error), Toast.LENGTH_SHORT).show();
}
}.toWorkerCallback()));
}
inputPanel.setVisibility(canSendMessages ? View.VISIBLE : View.GONE);
inputPanel.setEnabled(canSendMessages);
sendButton.setEnabled(canSendMessages);
attachButton.setEnabled(canSendMessages);
});
}
@@ -1754,7 +1816,9 @@ public class ConversationActivity extends PassphraseRequiredActivity
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
container.addOnKeyboardShownListener(this);
inputPanel.setListener(this);

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.conversation;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
@@ -54,6 +55,7 @@ public class ConversationBannerView extends ConstraintLayout {
public void setSubtitle(@Nullable CharSequence subtitle) {
contactSubtitle.setText(subtitle);
contactSubtitle.setVisibility(TextUtils.isEmpty(subtitle) ? GONE : VISIBLE);
}
public void setDescription(@Nullable CharSequence description) {

View File

@@ -77,8 +77,8 @@ import org.thoughtcrime.securesms.conversation.ConversationAdapter.ItemClickList
import org.thoughtcrime.securesms.conversation.ConversationAdapter.StickyHeaderViewHolder;
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -402,9 +402,11 @@ public class ConversationFragment extends LoggingFragment {
conversationBanner.setSubtitle(context.getResources()
.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, memberCount,
memberCount, pendingMemberCount));
} else {
} else if (memberCount > 0) {
conversationBanner.setSubtitle(context.getResources().getQuantityString(R.plurals.MessageRequestProfileView_members, memberCount,
memberCount));
} else {
conversationBanner.setSubtitle(null);
}
} else if (isSelf) {
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation));

View File

@@ -14,18 +14,30 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AsynchronousCallback;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
class ConversationGroupViewModel extends ViewModel {
import java.io.IOException;
private final MutableLiveData<Recipient> liveRecipient;
private final LiveData<GroupActiveState> groupActiveState;
final class ConversationGroupViewModel extends ViewModel {
private final MutableLiveData<Recipient> liveRecipient;
private final LiveData<GroupActiveState> groupActiveState;
private final LiveData<GroupDatabase.MemberLevel> selfMembershipLevel;
private ConversationGroupViewModel() {
liveRecipient = new MutableLiveData<>();
LiveData<GroupRecord> groupRecord = LiveDataUtil.mapAsync(liveRecipient, this::getGroupRecordForRecipient);
groupActiveState = Transformations.distinctUntilChanged(Transformations.map(groupRecord, this::mapToGroupActiveState));
this.liveRecipient = new MutableLiveData<>();
LiveData<GroupRecord> groupRecord = LiveDataUtil.mapAsync(liveRecipient, ConversationGroupViewModel::getGroupRecordForRecipient);
this.groupActiveState = Transformations.distinctUntilChanged(Transformations.map(groupRecord, ConversationGroupViewModel::mapToGroupActiveState));
this.selfMembershipLevel = Transformations.distinctUntilChanged(Transformations.map(groupRecord, ConversationGroupViewModel::mapToSelfMembershipLevel));
}
void onRecipientChange(Recipient recipient) {
@@ -36,7 +48,11 @@ class ConversationGroupViewModel extends ViewModel {
return groupActiveState;
}
private GroupRecord getGroupRecordForRecipient(Recipient recipient) {
LiveData<GroupDatabase.MemberLevel> getSelfMemberLevel() {
return selfMembershipLevel;
}
private static @Nullable GroupRecord getGroupRecordForRecipient(@Nullable Recipient recipient) {
if (recipient != null && recipient.isGroup()) {
Application context = ApplicationDependencies.getApplication();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
@@ -46,13 +62,37 @@ class ConversationGroupViewModel extends ViewModel {
}
}
private GroupActiveState mapToGroupActiveState(@Nullable GroupRecord record) {
private static GroupActiveState mapToGroupActiveState(@Nullable GroupRecord record) {
if (record == null) {
return null;
}
return new GroupActiveState(record.isActive(), record.isV2Group());
}
private static GroupDatabase.MemberLevel mapToSelfMembershipLevel(@Nullable GroupRecord record) {
if (record == null) {
return null;
}
return record.memberLevel(Recipient.self());
}
public static void onCancelJoinRequest(@NonNull Recipient recipient,
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
{
SignalExecutors.UNBOUNDED.execute(() -> {
if (!recipient.isPushV2Group()) {
throw new AssertionError();
}
try {
GroupManager.cancelJoinRequest(ApplicationDependencies.getApplication(), recipient.getGroupId().get().requireV2());
callback.onComplete(null);
} catch (GroupChangeFailedException | GroupChangeBusyException | IOException e) {
callback.onError(GroupChangeFailureReason.fromException(e));
}
});
}
static final class GroupActiveState {
private final boolean isActive;
private final boolean isActiveV2;