Add several callbacks to v2 convo fragment.

This commit is contained in:
Alex Hart
2023-04-14 15:40:26 -03:00
committed by Cody Henthorne
parent 279ad7945e
commit 9d17bf473c
9 changed files with 197 additions and 49 deletions

View File

@@ -7,7 +7,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EdgeEffect
import androidx.annotation.Discouraged
import androidx.annotation.LayoutRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
@@ -16,7 +15,6 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.WindowUtil
@Discouraged("The DSL API can be completely replaced by compose. See ComposeFragment or ComposeBottomSheetFragment for an alternative to this API")
abstract class DSLSettingsBottomSheetFragment(
@LayoutRes private val layoutId: Int = R.layout.dsl_settings_bottom_sheet,
val layoutManagerProducer: (Context) -> RecyclerView.LayoutManager = { context -> LinearLayoutManager(context) },

View File

@@ -46,6 +46,8 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewSwitcher;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
@@ -111,6 +113,7 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.conversation.quotes.MessageQuotesBottomSheet;
import org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSettingsDialog;
import org.thoughtcrime.securesms.conversation.v2.AddToContactsContract;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
@@ -216,7 +219,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private static final String TAG = Log.tag(ConversationFragment.class);
private static final int SCROLL_ANIMATION_THRESHOLD = 50;
private static final int CODE_ADD_EDIT_CONTACT = 77;
private static final int MAX_SCROLL_DELAY_COUNT = 5;
private final ActionModeCallback actionModeCallback = new ActionModeCallback();
@@ -271,6 +273,8 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private final DatabaseObserver.Observer threadDeletedObserver = this::onThreadDelete;
private ActivityResultLauncher<Intent> addToContactsLauncher;
public static void prepare(@NonNull Context context) {
FrameLayout parent = new FrameLayout(context);
parent.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
@@ -294,6 +298,8 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
addToContactsLauncher = registerForActivityResult(new AddToContactsContract(), result -> {});
disposables.bindTo(getViewLifecycleOwner());
lastSeenDisposable.bindTo(getViewLifecycleOwner());
@@ -1819,24 +1825,15 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
@Override
public void onAddToContactsClicked(@NonNull Contact contactWithAvatar) {
if (getContext() != null) {
new AsyncTask<Void, Void, Intent>() {
@Override
protected Intent doInBackground(Void... voids) {
return ContactUtil.buildAddToContactsIntent(getContext(), contactWithAvatar);
}
@Override
protected void onPostExecute(Intent intent) {
try {
startActivityForResult(intent, CODE_ADD_EDIT_CONTACT);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Could not locate contacts activity", e);
Toast.makeText(requireContext(), R.string.ConversationFragment__contacts_app_not_found, Toast.LENGTH_SHORT).show();
}
}
}.execute();
if (getContext() == null) {
return;
}
disposables.add(AddToContactsContract.createIntentAndLaunch(
ConversationFragment.this,
addToContactsLauncher,
contactWithAvatar
));
}
@Override
@@ -2149,15 +2146,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CODE_ADD_EDIT_CONTACT && getContext() != null) {
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(false));
}
}
private void handleEnterMultiSelect(@NonNull ConversationMessage conversationMessage) {
Set<MultiselectPart> multiselectParts = conversationMessage.getMultiselectCollection().toSet();

View File

@@ -114,7 +114,7 @@ public class ConversationRepository {
return new ConversationData(threadId, lastSeen, lastSeenPosition, lastScrolledPosition, jumpToPosition, threadSize, messageRequestData, showUniversalExpireTimerUpdate);
}
void markGiftBadgeRevealed(long messageId) {
public void markGiftBadgeRevealed(long messageId) {
SignalExecutors.BOUNDED_IO.execute(() -> {
List<MessageTable.MarkedMessageInfo> markedMessageInfo = SignalDatabase.messages().setOutgoingGiftsRevealed(Collections.singletonList(messageId));
if (!markedMessageInfo.isEmpty()) {

View File

@@ -0,0 +1,60 @@
package org.thoughtcrime.securesms.conversation.v2
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContract
import androidx.fragment.app.Fragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.kotlin.subscribeBy
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.contactshare.ContactUtil
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob
/**
* Wraps up the "Add shared contact to contact list" into a contract. The flow here is a little
* weird because buildAddToContactsIntent has to be run in the background (as it loads image data).
*
* Usage:
* Register for result from your fragment, and then pass the created launcher in when you call
* createIntentAndLaunch.
*/
class AddToContactsContract : ActivityResultContract<Intent, Unit>() {
override fun createIntent(context: Context, input: Intent): Intent = input
override fun parseResult(resultCode: Int, intent: Intent?) {
ApplicationDependencies.getJobManager().add(DirectoryRefreshJob(false))
}
companion object {
private val TAG = Log.tag(AddToContactsContract::class.java)
@JvmStatic
fun createIntentAndLaunch(
fragment: Fragment,
launcher: ActivityResultLauncher<Intent>,
contact: Contact
): Disposable {
return Single.fromCallable { ContactUtil.buildAddToContactsIntent(fragment.requireContext(), contact) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
try {
launcher.launch(it)
} catch (e: ActivityNotFoundException) {
Log.w(TAG, "Could not locate contacts activity", e)
Toast.makeText(fragment.requireContext(), R.string.ConversationFragment__contacts_app_not_found, Toast.LENGTH_SHORT).show()
}
}
}
}
}

View File

@@ -1,10 +1,14 @@
package org.thoughtcrime.securesms.conversation.v2
import android.app.ActivityOptions
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
@@ -14,6 +18,8 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
@@ -24,6 +30,8 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.LoggingFragment
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.gifts.viewgift.received.ViewReceivedGiftBottomSheet
import org.thoughtcrime.securesms.badges.gifts.viewgift.sent.ViewSentGiftBottomSheet
import org.thoughtcrime.securesms.components.ViewBinderDelegate
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner
@@ -55,12 +63,18 @@ import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionPlayerHolder
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ProjectionRecycler
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange
import org.thoughtcrime.securesms.groups.ui.GroupErrors
import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult
import org.thoughtcrime.securesms.invites.InviteActions
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory.create
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
import org.thoughtcrime.securesms.mms.AttachmentManager
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.notifications.v2.ConversationId
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity
import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment
import org.thoughtcrime.securesms.reactions.ReactionsBottomSheetDialogFragment
import org.thoughtcrime.securesms.recipients.Recipient
@@ -69,12 +83,14 @@ import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheet
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.ContextUtil
import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.FullscreenHelper
import org.thoughtcrime.securesms.util.WindowUtil
import org.thoughtcrime.securesms.util.fragments.requireListener
import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.util.visible
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil
@@ -117,8 +133,12 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
private lateinit var conversationOptionsMenuProvider: ConversationOptionsMenu.Provider
private lateinit var layoutManager: SmoothScrollingLinearLayoutManager
private lateinit var markReadHelper: MarkReadHelper
private lateinit var giphyMp4ProjectionRecycler: GiphyMp4ProjectionRecycler
private lateinit var addToContactsLauncher: ActivityResultLauncher<Intent>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
registerForResults()
conversationOptionsMenuProvider = ConversationOptionsMenu.Provider(ConversationOptionsMenuCallback(), disposables)
markReadHelper = MarkReadHelper(ConversationId.forConversation(args.threadId), requireContext(), viewLifecycleOwner)
@@ -166,6 +186,10 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
groupCallViewModel.peekGroupCall()
}
private fun registerForResults() {
addToContactsLauncher = registerForActivityResult(AddToContactsContract()) {}
}
private fun onFirstRecipientLoad(recipient: Recipient) {
Log.d(TAG, "onFirstRecipientLoad")
@@ -183,7 +207,7 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
adapter.setPagingController(viewModel.pagingController)
viewLifecycleOwner.lifecycle.addObserver(LastSeenPositionUpdater(adapter, layoutManager, viewModel))
binding.conversationItemRecycler.adapter = adapter
initializeGiphyMp4()
giphyMp4ProjectionRecycler = initializeGiphyMp4()
binding.conversationItemRecycler.addItemDecoration(
MultiselectItemDecoration(
@@ -266,7 +290,7 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
disposables += groupCallViewModel.hasActiveGroupCall.subscribeBy(onNext = {
// invalidateOptionsMenu
invalidateOptionsMenu()
binding.conversationGroupCallJoin.visible = it
})
@@ -296,6 +320,17 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
}
private fun handleBlockJoinRequest(recipient: Recipient) {
disposables += conversationGroupViewModel.blockJoinRequests(recipient).subscribeBy { result ->
if (result.isFailure()) {
val failureReason = GroupErrors.getUserDisplayMessage((result as GroupBlockJoinRequestResult.Failure).reason)
Toast.makeText(requireContext(), failureReason, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(requireContext(), R.string.ConversationFragment__blocked, Toast.LENGTH_SHORT).show()
}
}
}
private fun getVoiceNoteMediaController() = requireListener<VoiceNoteMediaControllerOwner>().voiceNoteMediaController
private fun initializeGiphyMp4(): GiphyMp4ProjectionRecycler {
@@ -309,13 +344,15 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
val callback = GiphyMp4ProjectionRecycler(holders)
GiphyMp4PlaybackController.attach(binding.conversationItemRecycler, callback, maxPlayback)
binding.conversationItemRecycler.addItemDecoration(GiphyMp4ItemDecoration(callback) { translationY: Float ->
// TODO [alex] reactionsShade.setTranslationY(translationY + list.getHeight())
}, 0)
binding.conversationItemRecycler.addItemDecoration(
GiphyMp4ItemDecoration(callback) { translationY: Float ->
// TODO [alex] reactionsShade.setTranslationY(translationY + list.getHeight())
},
0
)
return callback
}
private inner class ConversationItemClickListener : ConversationAdapter.ItemClickListener {
override fun onQuoteClicked(messageRecord: MmsMessageRecord?) {
// TODO [alex] - ("Not yet implemented")
@@ -352,7 +389,11 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
override fun onAddToContactsClicked(contact: Contact) {
// TODO [alex] - ("Not yet implemented")
disposables += AddToContactsContract.createIntentAndLaunch(
this@ConversationFragment,
addToContactsLauncher,
contact
)
}
override fun onMessageSharedContactClicked(choices: MutableList<Recipient>) {
@@ -374,12 +415,12 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
override fun onReactionClicked(multiselectPart: MultiselectPart, messageId: Long, isMms: Boolean) {
parentFragment ?: return
context ?: return
ReactionsBottomSheetDialogFragment.create(messageId, isMms).show(parentFragmentManager, null)
}
override fun onGroupMemberClicked(recipientId: RecipientId, groupId: GroupId) {
parentFragment ?: return
context ?: return
RecipientBottomSheetDialogFragment.create(recipientId, groupId).show(parentFragmentManager, "BOTTOM")
}
@@ -476,11 +517,21 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
override fun onBlockJoinRequest(recipient: Recipient) {
// TODO [alex] - ("Not yet implemented")
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.ConversationFragment__block_request)
.setMessage(getString(R.string.ConversationFragment__s_will_not_be_able_to_join_or_request_to_join_this_group_via_the_group_link, recipient.getDisplayName(requireContext())))
.setNegativeButton(R.string.ConversationFragment__cancel, null)
.setPositiveButton(R.string.ConversationFragment__block_request_button) { _, _ -> handleBlockJoinRequest(recipient) }
.show()
}
override fun onRecipientNameClicked(target: RecipientId) {
// TODO [alex] ("Not yet implemented")
context ?: return
disposables += viewModel.recipient.firstOrError().observeOn(AndroidSchedulers.mainThread()).subscribeBy {
RecipientBottomSheetDialogFragment.create(
target,
it.groupId.orElse(null)
).show(parentFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
}
}
override fun onInviteToSignalClicked() {
@@ -494,11 +545,16 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
override fun onActivatePaymentsClicked() {
// TODO [alex] -- ("Not yet implemented")
startActivity(Intent(requireContext(), PaymentsActivity::class.java))
}
override fun onSendPaymentClicked(recipientId: RecipientId) {
// TODO [alex] -- ("Not yet implemented")
disposables += viewModel.recipient
.firstOrError()
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy {
AttachmentManager.selectPayment(this@ConversationFragment, it)
}
}
override fun onScheduledIndicatorClicked(view: View, messageRecord: MessageRecord) {
@@ -511,15 +567,40 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
override fun onViewGiftBadgeClicked(messageRecord: MessageRecord) {
// TODO [alex] -- ("Not yet implemented")
if (!messageRecord.hasGiftBadge()) {
return
}
if (messageRecord.isOutgoing) {
ViewSentGiftBottomSheet.show(childFragmentManager, (messageRecord as MmsMessageRecord))
} else {
ViewReceivedGiftBottomSheet.show(childFragmentManager, (messageRecord as MmsMessageRecord))
}
}
override fun onGiftBadgeRevealed(messageRecord: MessageRecord) {
// TODO [alex] -- ("Not yet implemented")
viewModel.markGiftBadgeRevealed(messageRecord)
}
override fun goToMediaPreview(parent: ConversationItem?, sharedElement: View?, args: MediaIntentFactory.MediaPreviewArgs?) {
// TODO [alex] -- ("Not yet implemented")
override fun goToMediaPreview(parent: ConversationItem, sharedElement: View, args: MediaIntentFactory.MediaPreviewArgs) {
if (this@ConversationFragment.args.conversationScreenType.isInBubble) {
requireActivity().startActivity(create(requireActivity(), args.skipSharedElementTransition(true)))
return
}
if (args.isVideoGif) {
val adapterPosition: Int = binding.conversationItemRecycler.getChildAdapterPosition(parent)
val holder: GiphyMp4ProjectionPlayerHolder? = giphyMp4ProjectionRecycler.getCurrentHolder(adapterPosition)
if (holder != null) {
parent.showProjectionArea()
holder.hide()
}
}
sharedElement.transitionName = MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME
requireActivity().setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
val options = ActivityOptions.makeSceneTransitionAnimation(requireActivity(), sharedElement, MediaPreviewV2Activity.SHARED_ELEMENT_TRANSITION_NAME)
requireActivity().startActivity(create(requireActivity(), args), options.toBundle())
}
override fun onItemClick(item: MultiselectPart?) {

View File

@@ -124,4 +124,8 @@ class ConversationRepository(context: Context) {
fun setLastVisibleMessageTimestamp(threadId: Long, lastVisibleMessageTimestamp: Long) {
SignalExecutors.BOUNDED.submit { threads.setLastScrolled(threadId, lastVisibleMessageTimestamp) }
}
fun markGiftBadgeRevealed(messageId: Long) {
oldConversationRepository.markGiftBadgeRevealed(messageId)
}
}

View File

@@ -13,8 +13,10 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents.Args
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
import org.thoughtcrime.securesms.conversation.colors.NameColor
import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
/**
@@ -67,6 +69,12 @@ class ConversationViewModel(
)
}
fun markGiftBadgeRevealed(messageRecord: MessageRecord) {
if (messageRecord.isOutgoing && messageRecord.hasGiftBadge()) {
repository.markGiftBadgeRevealed(messageRecord.id)
}
}
class Factory(
private val args: Args,
private val repository: ConversationRepository

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2.groups
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
@@ -14,6 +15,8 @@ import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil
import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult
import org.thoughtcrime.securesms.groups.v2.GroupManagementRepository
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -22,7 +25,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId
* Manages group state and actions for conversations.
*/
class ConversationGroupViewModel(
private val threadId: Long
private val threadId: Long,
private val groupManagementRepository: GroupManagementRepository = GroupManagementRepository()
) : ViewModel() {
private val disposables = CompositeDisposable()
@@ -83,6 +87,12 @@ class ConversationGroupViewModel(
return memberLevel.groupTableMemberLevel != GroupTable.MemberLevel.ADMINISTRATOR && memberLevel.isAnnouncementGroup
}
fun blockJoinRequests(recipient: Recipient): Single<GroupBlockJoinRequestResult> {
return _recipient.firstOrError().flatMap {
groupManagementRepository.blockJoinRequests(it.requireGroupId().requireV2(), recipient)
}.observeOn(AndroidSchedulers.mainThread())
}
private fun getActionableRequestingMembersCount(groupRecord: GroupRecord): Int {
return if (groupRecord.isV2Group && groupRecord.memberLevel(Recipient.self()) == GroupTable.MemberLevel.ADMINISTRATOR) {
groupRecord.requireV2GroupProperties()

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.InputPanel xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/bottom_panel"
android:layout_width="match_parent"