Renamed group terminology

This commit is contained in:
SessionHero01 2024-10-25 10:26:18 +11:00
parent 122838d9ae
commit e89cbdf029
No known key found for this signature in database
49 changed files with 210 additions and 242 deletions

View File

@ -440,7 +440,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
} }
private void sendMediaSavedNotificationIfNeeded() { private void sendMediaSavedNotificationIfNeeded() {
if (conversationRecipient.isGroupRecipient()) return; if (conversationRecipient.isGroupOrCommunityRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset())); DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset()));
MessageSender.send(message, conversationRecipient.getAddress()); MessageSender.send(message, conversationRecipient.getAddress());
} }

View File

@ -53,7 +53,7 @@ class ProfilePictureView @JvmOverloads constructor(
fun update(recipient: Recipient) { fun update(recipient: Recipient) {
this.recipient = recipient this.recipient = recipient
recipient.run { update(address, isLegacyClosedGroupRecipient, isOpenGroupInboxRecipient) } recipient.run { update(address, isLegacyGroupRecipient, isCommunityInboxRecipient) }
} }
fun update( fun update(

View File

@ -298,7 +298,7 @@ class ConfigToDatabaseSync @Inject constructor(
buildMap(reader.count) { buildMap(reader.count) {
var current = reader.next var current = reader.next
while (current != null) { while (current != null) {
if (current.recipient?.isClosedGroupV2Recipient == true) { if (current.recipient?.isGroupV2Recipient == true) {
put(AccountId(current.recipient.address.serialize()), current.threadId) put(AccountId(current.recipient.address.serialize()), current.threadId)
} }

View File

@ -33,7 +33,7 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
} }
val list = mutableListOf<ContactSelectionListItem>() val list = mutableListOf<ContactSelectionListItem>()
if (isFlagSet(DisplayMode.FLAG_CLOSED_GROUPS)) { if (isFlagSet(DisplayMode.FLAG_CLOSED_GROUPS)) {
list.addAll(getClosedGroups(contacts)) list.addAll(getGroups(contacts))
} }
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) { if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
list.addAll(getCommunities(contacts)) list.addAll(getCommunities(contacts))
@ -46,14 +46,12 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
private fun getContacts(contacts: List<Recipient>): List<ContactSelectionListItem> { private fun getContacts(contacts: List<Recipient>): List<ContactSelectionListItem> {
return getItems(contacts, context.getString(R.string.contactContacts)) { return getItems(contacts, context.getString(R.string.contactContacts)) {
!it.isGroupRecipient !it.isGroupOrCommunityRecipient
} }
} }
private fun getClosedGroups(contacts: List<Recipient>): List<ContactSelectionListItem> { private fun getGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
return getItems(contacts, context.getString(R.string.conversationsGroups)) { return getItems(contacts, context.getString(R.string.conversationsGroups)) { it.address.isGroup }
it.address.isLegacyClosedGroup || it.address.isClosedGroupV2
}
} }
private fun getCommunities(contacts: List<Recipient>): List<ContactSelectionListItem> { private fun getCommunities(contacts: List<Recipient>): List<ContactSelectionListItem> {

View File

@ -9,7 +9,7 @@ class SelectContactsLoader(context: Context, private val usersToExclude: Set<Str
override fun loadInBackground(): List<String> { override fun loadInBackground(): List<String> {
val contacts = ContactUtilities.getAllContacts(context) val contacts = ContactUtilities.getAllContacts(context)
return contacts.filter { return contacts.filter {
!it.isGroupRecipient && !usersToExclude.contains(it.address.toString()) && it.hasApprovedMe() !it.isGroupOrCommunityRecipient && !usersToExclude.contains(it.address.toString()) && it.hasApprovedMe()
}.map { }.map {
it.address.toString() it.address.toString()
} }

View File

@ -56,7 +56,7 @@ class UserView : LinearLayout {
val address = user.address.serialize() val address = user.address.serialize()
binding.profilePictureView.update(user) binding.profilePictureView.update(user)
binding.actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24) binding.actionIndicatorImageView.setImageResource(R.drawable.ic_baseline_edit_24)
binding.nameTextView.text = if (user.isGroupRecipient) user.name else getUserDisplayName(address) binding.nameTextView.text = if (user.isGroupOrCommunityRecipient) user.name else getUserDisplayName(address)
when (actionIndicator) { when (actionIndicator) {
ActionIndicator.None -> { ActionIndicator.None -> {
binding.actionIndicatorImageView.visibility = View.GONE binding.actionIndicatorImageView.visibility = View.GONE

View File

@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlin.time.Duration.Companion.milliseconds
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewConversationActionBarBinding import network.loki.messenger.databinding.ViewConversationActionBarBinding
import network.loki.messenger.databinding.ViewConversationSettingBinding import network.loki.messenger.databinding.ViewConversationSettingBinding
@ -81,7 +80,7 @@ class ConversationActionBarView @JvmOverloads constructor(
) { ) {
this.delegate = delegate this.delegate = delegate
binding.profilePictureView.layoutParams = resources.getDimensionPixelSize( binding.profilePictureView.layoutParams = resources.getDimensionPixelSize(
if (recipient.isClosedGroupV2Recipient) R.dimen.medium_profile_picture_size else R.dimen.small_profile_picture_size if (recipient.isGroupV2Recipient) R.dimen.medium_profile_picture_size else R.dimen.small_profile_picture_size
).let { LayoutParams(it, it) } ).let { LayoutParams(it, it) }
update(recipient, openGroup, config) update(recipient, openGroup, config)
} }
@ -134,12 +133,12 @@ class ConversationActionBarView @JvmOverloads constructor(
) )
} }
if (recipient.isGroupRecipient) { if (recipient.isGroupOrCommunityRecipient) {
val title = if (recipient.isCommunityRecipient) { val title = if (recipient.isCommunityRecipient) {
val userCount = openGroup?.let { lokiApiDb.getUserCount(it.room, it.server) } ?: 0 val userCount = openGroup?.let { lokiApiDb.getUserCount(it.room, it.server) } ?: 0
resources.getQuantityString(R.plurals.membersActive, userCount, userCount) resources.getQuantityString(R.plurals.membersActive, userCount, userCount)
} else { } else {
val userCount = if (recipient.isClosedGroupV2Recipient) { val userCount = if (recipient.isGroupV2Recipient) {
storage.getMembers(recipient.address.serialize()).size storage.getMembers(recipient.address.serialize()).size
} else { // legacy closed groups } else { // legacy closed groups
groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size groupDb.getGroupMemberAddresses(recipient.address.toGroupString(), true).size

View File

@ -58,7 +58,7 @@ class DisappearingMessages @Inject constructor(
text = if (message.expiresIn == 0L) R.string.confirm else R.string.set, text = if (message.expiresIn == 0L) R.string.confirm else R.string.set,
contentDescriptionRes = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_setButton contentDescriptionRes = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_setButton
) { ) {
set(message.threadId, message.recipient.address, message.expiryMode, message.recipient.isClosedGroupV2Recipient) set(message.threadId, message.recipient.address, message.expiryMode, message.recipient.isGroupV2Recipient)
} }
cancelButton() cancelButton()
} }

View File

@ -60,25 +60,25 @@ class DisappearingMessagesViewModel(
viewModelScope.launch { viewModelScope.launch {
val expiryMode = storage.getExpirationConfiguration(threadId)?.expiryMode ?: ExpiryMode.NONE val expiryMode = storage.getExpirationConfiguration(threadId)?.expiryMode ?: ExpiryMode.NONE
val recipient = threadDb.getRecipientForThreadId(threadId)?: return@launch val recipient = threadDb.getRecipientForThreadId(threadId)?: return@launch
val groupRecord = recipient.takeIf { it.isLegacyClosedGroupRecipient || it.isClosedGroupV2Recipient } val groupRecord = recipient.takeIf { it.isLegacyGroupRecipient || it.isGroupV2Recipient }
?.run { groupDb.getGroup(address.toGroupString()).orNull() } ?.run { groupDb.getGroup(address.toGroupString()).orNull() }
val isAdmin = when { val isAdmin = when {
recipient.isClosedGroupV2Recipient -> { recipient.isGroupV2Recipient -> {
// Handle the new closed group functionality // Handle the new closed group functionality
storage.getMembers(recipient.address.serialize()).any { it.sessionId == textSecurePreferences.getLocalNumber() && it.admin } storage.getMembers(recipient.address.serialize()).any { it.sessionId == textSecurePreferences.getLocalNumber() && it.admin }
} }
recipient.isLegacyClosedGroupRecipient -> { recipient.isLegacyGroupRecipient -> {
// Handle as legacy group // Handle as legacy group
groupRecord?.admins?.any{ it.serialize() == textSecurePreferences.getLocalNumber() } == true groupRecord?.admins?.any{ it.serialize() == textSecurePreferences.getLocalNumber() } == true
} }
else -> !recipient.isGroupRecipient else -> !recipient.isGroupOrCommunityRecipient
} }
_state.update { _state.update {
it.copy( it.copy(
address = recipient.address, address = recipient.address,
isGroup = recipient.isGroupRecipient, isGroup = recipient.isGroupOrCommunityRecipient,
isNoteToSelf = recipient.address.serialize() == textSecurePreferences.getLocalNumber(), isNoteToSelf = recipient.address.serialize() == textSecurePreferences.getLocalNumber(),
isSelfAdmin = isAdmin, isSelfAdmin = isAdmin,
expiryMode = expiryMode, expiryMode = expiryMode,

View File

@ -37,7 +37,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -57,17 +56,13 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.R import network.loki.messenger.R
@ -168,8 +163,6 @@ import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.giph.ui.GiphyActivity
import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.search.getSearchName import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.home.HomeActivity
import org.thoughtcrime.securesms.home.startHomeActivity
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
@ -188,7 +181,6 @@ import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.ActivityDispatcher import org.thoughtcrime.securesms.util.ActivityDispatcher
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.NetworkUtils import org.thoughtcrime.securesms.util.NetworkUtils
@ -815,7 +807,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// called from onCreate // called from onCreate
private fun setUpBlockedBanner() { private fun setUpBlockedBanner() {
val recipient = viewModel.recipient?.takeUnless { it.isGroupRecipient } ?: return val recipient = viewModel.recipient?.takeUnless { it.isGroupOrCommunityRecipient } ?: return
binding.blockedBannerTextView.text = applicationContext.getString(R.string.blockBlockedDescription) binding.blockedBannerTextView.text = applicationContext.getString(R.string.blockBlockedDescription)
binding.blockedBanner.isVisible = recipient.isBlocked binding.blockedBanner.isVisible = recipient.isBlocked
binding.blockedBanner.setOnClickListener { viewModel.unblock() } binding.blockedBanner.setOnClickListener { viewModel.unblock() }
@ -838,7 +830,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
private fun setUpLegacyGroupBanner() { private fun setUpLegacyGroupBanner() {
val shouldDisplayBanner = viewModel.recipient?.isLegacyClosedGroupRecipient ?: return val shouldDisplayBanner = viewModel.recipient?.isLegacyGroupRecipient ?: return
with(binding) { with(binding) {
outdatedGroupBanner.isVisible = shouldDisplayBanner outdatedGroupBanner.isVisible = shouldDisplayBanner
@ -1190,7 +1182,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
// 10n1 and groups // 10n1 and groups
recipient.is1on1 || recipient.isGroupRecipient -> { recipient.is1on1 || recipient.isGroupOrCommunityRecipient -> {
Phrase.from(applicationContext, R.string.groupNoMessages) Phrase.from(applicationContext, R.string.groupNoMessages)
.put(GROUP_NAME_KEY, recipient.toShortString()) .put(GROUP_NAME_KEY, recipient.toShortString())
.format() .format()
@ -1241,7 +1233,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for block action") val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for block action")
val invitingAdmin = viewModel.invitingAdmin val invitingAdmin = viewModel.invitingAdmin
val name = if (recipient.isClosedGroupV2Recipient && invitingAdmin != null) { val name = if (recipient.isGroupV2Recipient && invitingAdmin != null) {
invitingAdmin.getSearchName() invitingAdmin.getSearchName()
} else { } else {
recipient.toShortString() recipient.toShortString()
@ -1291,7 +1283,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// TODO: don't need to allow new closed group check here, removed in new disappearing messages // TODO: don't need to allow new closed group check here, removed in new disappearing messages
override fun showDisappearingMessages(thread: Recipient) { override fun showDisappearingMessages(thread: Recipient) {
if (thread.isLegacyClosedGroupRecipient) { if (thread.isLegacyGroupRecipient) {
groupDb.getGroup(thread.address.toGroupString()).orNull()?.run { if (!isActive) return } groupDb.getGroup(thread.address.toGroupString()).orNull()?.run { if (!isActive) return }
} }
Intent(this, DisappearingMessagesActivity::class.java) Intent(this, DisappearingMessagesActivity::class.java)
@ -1719,7 +1711,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
override fun onReactionLongClicked(messageId: MessageId, emoji: String?) { override fun onReactionLongClicked(messageId: MessageId, emoji: String?) {
if (viewModel.recipient?.isGroupRecipient == true) { if (viewModel.recipient?.isGroupOrCommunityRecipient == true) {
val isUserModerator = viewModel.openGroup?.let { openGroup -> val isUserModerator = viewModel.openGroup?.let { openGroup ->
val userPublicKey = textSecurePreferences.getLocalNumber() ?: return@let false val userPublicKey = textSecurePreferences.getLocalNumber() ?: return@let false
OpenGroupManager.isUserModerator(this, openGroup.id, userPublicKey, viewModel.blindedPublicKey) OpenGroupManager.isUserModerator(this, openGroup.id, userPublicKey, viewModel.blindedPublicKey)
@ -2339,7 +2331,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun sendScreenshotNotification() { private fun sendScreenshotNotification() {
val recipient = viewModel.recipient ?: return val recipient = viewModel.recipient ?: return
if (recipient.isGroupRecipient) return if (recipient.isGroupOrCommunityRecipient) return
val kind = DataExtractionNotification.Kind.Screenshot() val kind = DataExtractionNotification.Kind.Screenshot()
val message = DataExtractionNotification(kind) val message = DataExtractionNotification(kind)
MessageSender.send(message, recipient.address) MessageSender.send(message, recipient.address)
@ -2347,7 +2339,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun sendMediaSavedNotification() { private fun sendMediaSavedNotification() {
val recipient = viewModel.recipient ?: return val recipient = viewModel.recipient ?: return
if (recipient.isGroupRecipient) { return } if (recipient.isGroupOrCommunityRecipient) { return }
val timestamp = SnodeAPI.nowWithOffset val timestamp = SnodeAPI.nowWithOffset
val kind = DataExtractionNotification.Kind.MediaSaved(timestamp) val kind = DataExtractionNotification.Kind.MediaSaved(timestamp)
val message = DataExtractionNotification(kind) val message = DataExtractionNotification(kind)

View File

@ -123,8 +123,8 @@ class ConversationViewModel(
val blindedRecipient: Recipient? val blindedRecipient: Recipient?
get() = _recipient.value?.let { recipient -> get() = _recipient.value?.let { recipient ->
when { when {
recipient.isOpenGroupOutboxRecipient -> recipient recipient.isCommunityOutboxRecipient -> recipient
recipient.isOpenGroupInboxRecipient -> repository.maybeGetBlindedRecipient(recipient) recipient.isCommunityInboxRecipient -> repository.maybeGetBlindedRecipient(recipient)
else -> null else -> null
} }
} }
@ -137,7 +137,7 @@ class ConversationViewModel(
val invitingAdmin: Recipient? val invitingAdmin: Recipient?
get() { get() {
val recipient = recipient ?: return null val recipient = recipient ?: return null
if (!recipient.isClosedGroupV2Recipient) return null if (!recipient.isGroupV2Recipient) return null
return repository.getInvitingAdmin(threadId) return repository.getInvitingAdmin(threadId)
} }
@ -151,14 +151,14 @@ class ConversationViewModel(
private val closedGroupMembers: List<GroupMember> private val closedGroupMembers: List<GroupMember>
get() { get() {
val recipient = recipient ?: return emptyList() val recipient = recipient ?: return emptyList()
if (!recipient.isClosedGroupV2Recipient) return emptyList() if (!recipient.isGroupV2Recipient) return emptyList()
return storage.getMembers(recipient.address.serialize()) return storage.getMembers(recipient.address.serialize())
} }
val isClosedGroupAdmin: Boolean val isClosedGroupAdmin: Boolean
get() { get() {
val recipient = recipient ?: return false val recipient = recipient ?: return false
return !recipient.isClosedGroupV2Recipient || return !recipient.isGroupV2Recipient ||
(closedGroupMembers.firstOrNull { it.sessionId == storage.getUserPublicKey() }?.admin ?: false) (closedGroupMembers.firstOrNull { it.sessionId == storage.getUserPublicKey() }?.admin ?: false)
} }
@ -174,7 +174,7 @@ class ConversationViewModel(
val isMessageRequestThread : Boolean val isMessageRequestThread : Boolean
get() { get() {
val recipient = recipient ?: return false val recipient = recipient ?: return false
return !recipient.isLocalNumber && !recipient.isLegacyClosedGroupRecipient && !recipient.isCommunityRecipient && !recipient.isApproved return !recipient.isLocalNumber && !recipient.isLegacyGroupRecipient && !recipient.isCommunityRecipient && !recipient.isApproved
} }
val canReactToMessages: Boolean val canReactToMessages: Boolean
@ -230,8 +230,8 @@ class ConversationViewModel(
*/ */
private fun shouldShowInput(recipient: Recipient?): Boolean { private fun shouldShowInput(recipient: Recipient?): Boolean {
return when { return when {
recipient?.isClosedGroupV2Recipient == true -> !repository.isGroupReadOnly(recipient) recipient?.isGroupV2Recipient == true -> !repository.isGroupReadOnly(recipient)
recipient?.isLegacyClosedGroupRecipient == true -> { recipient?.isLegacyGroupRecipient == true -> {
groupDb.getGroup(recipient.address.toGroupString()).orNull()?.isActive == true groupDb.getGroup(recipient.address.toGroupString()).orNull()?.isActive == true
} }
openGroup != null -> openGroup?.canWrite == true openGroup != null -> openGroup?.canWrite == true
@ -253,7 +253,7 @@ class ConversationViewModel(
(!recipient.isApproved && !recipient.isLocalNumber) && (!recipient.isApproved && !recipient.isLocalNumber) &&
// Req 4: the type of conversation supports message request // Req 4: the type of conversation supports message request
(recipient.is1on1 || recipient.isClosedGroupV2Recipient) && (recipient.is1on1 || recipient.isGroupV2Recipient) &&
// Req 2: we haven't sent a message to them before // Req 2: we haven't sent a message to them before
!threadDb.getLastSeenAndHasSent(threadId).second() && !threadDb.getLastSeenAndHasSent(threadId).second() &&
@ -263,14 +263,14 @@ class ConversationViewModel(
) { ) {
return MessageRequestUiState.Visible( return MessageRequestUiState.Visible(
acceptButtonText = if (recipient.isGroupRecipient) { acceptButtonText = if (recipient.isGroupOrCommunityRecipient) {
R.string.messageRequestGroupInviteDescription R.string.messageRequestGroupInviteDescription
} else { } else {
R.string.messageRequestsAcceptDescription R.string.messageRequestsAcceptDescription
}, },
// You can block a 1to1 conversation, or a normal groups v2 conversation // You can block a 1to1 conversation, or a normal groups v2 conversation
showBlockButton = recipient.is1on1 || recipient.isClosedGroupV2Recipient, showBlockButton = recipient.is1on1 || recipient.isGroupV2Recipient,
declineButtonText = if (recipient.isClosedGroupV2Recipient) { declineButtonText = if (recipient.isGroupV2Recipient) {
R.string.delete R.string.delete
} else { } else {
R.string.decline R.string.decline
@ -311,7 +311,7 @@ class ConversationViewModel(
fun block() { fun block() {
// inviting admin will be true if this request is a closed group message request // inviting admin will be true if this request is a closed group message request
val recipient = invitingAdmin ?: recipient ?: return Log.w("Loki", "Recipient was null for block action") val recipient = invitingAdmin ?: recipient ?: return Log.w("Loki", "Recipient was null for block action")
if (recipient.isContactRecipient || recipient.isClosedGroupV2Recipient) { if (recipient.isContactRecipient || recipient.isGroupV2Recipient) {
repository.setBlocked(threadId, recipient, true) repository.setBlocked(threadId, recipient, true)
} }
} }

View File

@ -12,7 +12,6 @@ import network.loki.messenger.databinding.FragmentDeleteMessageBottomSheetBindin
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.database.SessionContactDatabase import org.thoughtcrime.securesms.database.SessionContactDatabase
import org.thoughtcrime.securesms.util.UiModeUtilities
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -59,11 +58,11 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen
if (recipient.isLocalNumber) { if (recipient.isLocalNumber) {
binding.deleteForEveryoneTextView.text = binding.deleteForEveryoneTextView.text =
getString(R.string.clearMessagesForMe) getString(R.string.clearMessagesForMe)
} else if (!recipient.isGroupRecipient && !contact.isNullOrEmpty()) { } else if (!recipient.isGroupOrCommunityRecipient && !contact.isNullOrEmpty()) {
binding.deleteForEveryoneTextView.text = binding.deleteForEveryoneTextView.text =
resources.getString(R.string.clearMessagesForEveryone) resources.getString(R.string.clearMessagesForEveryone)
} }
binding.deleteForEveryoneTextView.isVisible = !recipient.isLegacyClosedGroupRecipient binding.deleteForEveryoneTextView.isVisible = !recipient.isLegacyGroupRecipient
binding.deleteForMeTextView.setOnClickListener(this) binding.deleteForMeTextView.setOnClickListener(this)
binding.deleteForEveryoneTextView.setOnClickListener(this) binding.deleteForEveryoneTextView.setOnClickListener(this)
binding.cancelTextView.setOnClickListener(this) binding.cancelTextView.setOnClickListener(this)

View File

@ -1,11 +1,7 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.app.Dialog import android.app.Dialog
import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.squareup.phrase.Phrase import com.squareup.phrase.Phrase
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -39,8 +35,8 @@ class AutoDownloadDialog(private val threadRecipient: Recipient,
val displayName = when { val displayName = when {
threadRecipient.isCommunityRecipient -> storage.getOpenGroup(threadId)?.name ?: "UNKNOWN" threadRecipient.isCommunityRecipient -> storage.getOpenGroup(threadId)?.name ?: "UNKNOWN"
threadRecipient.isLegacyClosedGroupRecipient -> storage.getGroup(threadRecipient.address.toGroupString())?.title ?: "UNKNOWN" threadRecipient.isLegacyGroupRecipient -> storage.getGroup(threadRecipient.address.toGroupString())?.title ?: "UNKNOWN"
threadRecipient.isClosedGroupV2Recipient -> threadRecipient.name ?: "UNKNOWN" threadRecipient.isGroupV2Recipient -> threadRecipient.name ?: "UNKNOWN"
else -> storage.getContactWithAccountID(threadRecipient.address.serialize())?.displayName(Contact.ContactContext.REGULAR) ?: "UNKNOWN" else -> storage.getContactWithAccountID(threadRecipient.address.serialize())?.displayName(Contact.ContactContext.REGULAR) ?: "UNKNOWN"
} }
title(getString(R.string.attachmentsAutoDownloadModalTitle)) title(getString(R.string.attachmentsAutoDownloadModalTitle))

View File

@ -85,11 +85,11 @@ class MentionViewModel(
} }
val memberIDs = when { val memberIDs = when {
recipient.isLegacyClosedGroupRecipient -> { recipient.isLegacyGroupRecipient -> {
groupDatabase.getGroupMemberAddresses(recipient.address.toGroupString(), false) groupDatabase.getGroupMemberAddresses(recipient.address.toGroupString(), false)
.map { it.serialize() } .map { it.serialize() }
} }
recipient.isClosedGroupV2Recipient -> { recipient.isGroupV2Recipient -> {
storage.getMembers(recipient.address.serialize()).map { it.sessionId } storage.getMembers(recipient.address.serialize()).map { it.sessionId }
} }

View File

@ -10,7 +10,6 @@ import org.session.libsession.messaging.utilities.SodiumUtilities
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.IdPrefix
import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.AccountId
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter import org.thoughtcrime.securesms.conversation.v2.ConversationAdapter
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
@ -64,7 +63,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
menu.findItem(R.id.menu_context_copy).isVisible = !containsControlMessage && hasText menu.findItem(R.id.menu_context_copy).isVisible = !containsControlMessage && hasText
// Copy Account ID // Copy Account ID
menu.findItem(R.id.menu_context_copy_public_key).isVisible = menu.findItem(R.id.menu_context_copy_public_key).isVisible =
(thread.isGroupRecipient && !thread.isCommunityRecipient && selectedItems.size == 1 && firstMessage.individualRecipient.address.toString() != userPublicKey) (thread.isGroupOrCommunityRecipient && !thread.isCommunityRecipient && selectedItems.size == 1 && firstMessage.individualRecipient.address.toString() != userPublicKey)
// Message detail // Message detail
menu.findItem(R.id.menu_message_details).isVisible = selectedItems.size == 1 menu.findItem(R.id.menu_message_details).isVisible = selectedItems.size == 1
// Resend // Resend

View File

@ -44,7 +44,6 @@ import org.thoughtcrime.securesms.calls.WebRtcCallActivity
import org.thoughtcrime.securesms.contacts.SelectContactsActivity import org.thoughtcrime.securesms.contacts.SelectContactsActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.groups.EditGroupActivity import org.thoughtcrime.securesms.groups.EditGroupActivity
@ -75,7 +74,7 @@ object ConversationMenuHelper {
// Base menu (options that should always be present) // Base menu (options that should always be present)
inflater.inflate(R.menu.menu_conversation, menu) inflater.inflate(R.menu.menu_conversation, menu)
// Expiring messages // Expiring messages
if (!isCommunity && (thread.hasApprovedMe() || thread.isLegacyClosedGroupRecipient || thread.isLocalNumber)) { if (!isCommunity && (thread.hasApprovedMe() || thread.isLegacyGroupRecipient || thread.isLocalNumber)) {
inflater.inflate(R.menu.menu_conversation_expiration, menu) inflater.inflate(R.menu.menu_conversation_expiration, menu)
} }
// One-on-one chat menu allows copying the account id // One-on-one chat menu allows copying the account id
@ -91,12 +90,12 @@ object ConversationMenuHelper {
} }
} }
// (Legacy) Closed group menu (options that should only be present in closed groups) // (Legacy) Closed group menu (options that should only be present in closed groups)
if (thread.isLegacyClosedGroupRecipient) { if (thread.isLegacyGroupRecipient) {
inflater.inflate(R.menu.menu_conversation_legacy_group, menu) inflater.inflate(R.menu.menu_conversation_legacy_group, menu)
} }
// Groups v2 menu // Groups v2 menu
if (thread.isClosedGroupV2Recipient) { if (thread.isGroupV2Recipient) {
val hasAdminKey = configFactory.withUserConfigs { it.userGroups.getClosedGroup(thread.address.serialize())?.hasAdminKey() } val hasAdminKey = configFactory.withUserConfigs { it.userGroups.getClosedGroup(thread.address.serialize())?.hasAdminKey() }
if (hasAdminKey == true) { if (hasAdminKey == true) {
inflater.inflate(R.menu.menu_conversation_groups_v2_admin, menu) inflater.inflate(R.menu.menu_conversation_groups_v2_admin, menu)
@ -116,7 +115,7 @@ object ConversationMenuHelper {
inflater.inflate(R.menu.menu_conversation_unmuted, menu) inflater.inflate(R.menu.menu_conversation_unmuted, menu)
} }
if (thread.isGroupRecipient && !thread.isMuted) { if (thread.isGroupOrCommunityRecipient && !thread.isMuted) {
inflater.inflate(R.menu.menu_conversation_notification_settings, menu) inflater.inflate(R.menu.menu_conversation_notification_settings, menu)
} }
@ -260,7 +259,7 @@ object ConversationMenuHelper {
} }
} }
if (icon == null) { if (icon == null) {
icon = IconCompat.createWithResource(context, if (thread.isGroupRecipient) R.mipmap.ic_group_shortcut else R.mipmap.ic_person_shortcut) icon = IconCompat.createWithResource(context, if (thread.isGroupOrCommunityRecipient) R.mipmap.ic_group_shortcut else R.mipmap.ic_person_shortcut)
} }
return icon return icon
} }
@ -319,11 +318,11 @@ object ConversationMenuHelper {
private fun editClosedGroup(context: Context, thread: Recipient) { private fun editClosedGroup(context: Context, thread: Recipient) {
when { when {
thread.isClosedGroupV2Recipient -> { thread.isGroupV2Recipient -> {
context.startActivity(EditGroupActivity.createIntent(context, thread.address.serialize())) context.startActivity(EditGroupActivity.createIntent(context, thread.address.serialize()))
} }
thread.isLegacyClosedGroupRecipient -> { thread.isLegacyGroupRecipient -> {
val intent = Intent(context, EditLegacyGroupActivity::class.java) val intent = Intent(context, EditLegacyGroupActivity::class.java)
val groupID: String = thread.address.toGroupString() val groupID: String = thread.address.toGroupString()
intent.putExtra(groupIDKey, groupID) intent.putExtra(groupIDKey, groupID)
@ -341,7 +340,7 @@ object ConversationMenuHelper {
groupManager: GroupManagerV2, groupManager: GroupManagerV2,
): ReceiveChannel<Unit>? { ): ReceiveChannel<Unit>? {
when { when {
thread.isLegacyClosedGroupRecipient -> { thread.isLegacyGroupRecipient -> {
val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull() val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull()
val admins = group.admins val admins = group.admins
val accountID = TextSecurePreferences.getLocalNumber(context) val accountID = TextSecurePreferences.getLocalNumber(context)
@ -364,7 +363,7 @@ object ConversationMenuHelper {
) )
} }
thread.isClosedGroupV2Recipient -> { thread.isGroupV2Recipient -> {
val accountId = AccountId(thread.address.serialize()) val accountId = AccountId(thread.address.serialize())
val group = configFactory.withUserConfigs { it.userGroups.getClosedGroup(accountId.hexString) } ?: return null val group = configFactory.withUserConfigs { it.userGroups.getClosedGroup(accountId.hexString) } ?: return null
val name = configFactory.withGroupConfigs(accountId) { val name = configFactory.withGroupConfigs(accountId) {

View File

@ -4,7 +4,6 @@ import android.Manifest
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
@ -12,7 +11,6 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.squareup.phrase.Phrase
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewControlMessageBinding import network.loki.messenger.databinding.ViewControlMessageBinding
@ -78,7 +76,7 @@ class ControlMessageView : LinearLayout {
val threadRecipient = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(message.threadId) val threadRecipient = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(message.threadId)
if (threadRecipient?.isClosedGroupV2Recipient == true) { if (threadRecipient?.isGroupV2Recipient == true) {
expirationTimerView.setTimerIcon() expirationTimerView.setTimerIcon()
} else { } else {
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn) expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
@ -87,7 +85,7 @@ class ControlMessageView : LinearLayout {
followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled
&& !message.isOutgoing && !message.isOutgoing
&& message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE) && message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE)
&& threadRecipient?.isGroupRecipient != true && threadRecipient?.isGroupOrCommunityRecipient != true
if (followSetting.isVisible) { if (followSetting.isVisible) {
binding.controlContentView.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) } binding.controlContentView.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) }

View File

@ -16,7 +16,6 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
@ -61,7 +60,6 @@ import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.UserDetailsBottomSheet import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager import com.bumptech.glide.RequestManager
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.disableClipping
@ -155,14 +153,14 @@ class VisibleMessageView : FrameLayout {
replyDisabled = message.isOpenGroupInvitation replyDisabled = message.isOpenGroupInvitation
val threadID = message.threadId val threadID = message.threadId
val thread = threadDb.getRecipientForThreadId(threadID) ?: return val thread = threadDb.getRecipientForThreadId(threadID) ?: return
val isGroupThread = thread.isGroupRecipient val isGroupThread = thread.isGroupOrCommunityRecipient
val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread) val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread)
val isEndOfMessageCluster = isEndOfMessageCluster(message, next, isGroupThread) val isEndOfMessageCluster = isEndOfMessageCluster(message, next, isGroupThread)
// Show profile picture and sender name if this is a group thread AND the message is incoming // Show profile picture and sender name if this is a group thread AND the message is incoming
binding.moderatorIconImageView.isVisible = false binding.moderatorIconImageView.isVisible = false
binding.profilePictureView.visibility = when { binding.profilePictureView.visibility = when {
thread.isGroupRecipient && !message.isOutgoing && isEndOfMessageCluster -> View.VISIBLE thread.isGroupOrCommunityRecipient && !message.isOutgoing && isEndOfMessageCluster -> View.VISIBLE
thread.isGroupRecipient -> View.INVISIBLE thread.isGroupOrCommunityRecipient -> View.INVISIBLE
else -> View.GONE else -> View.GONE
} }

View File

@ -34,7 +34,7 @@ object ResendMessageUtilities {
message.text = messageRecord.body message.text = messageRecord.body
} }
message.sentTimestamp = messageRecord.timestamp message.sentTimestamp = messageRecord.timestamp
if (recipient.isGroupRecipient) { if (recipient.isGroupOrCommunityRecipient) {
message.groupPublicKey = recipient.address.toGroupString() message.groupPublicKey = recipient.address.toGroupString()
} else { } else {
message.recipient = messageRecord.recipient.address.serialize() message.recipient = messageRecord.recipient.address.serialize()

View File

@ -162,7 +162,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
val ourAddress = messageId.address val ourAddress = messageId.address
val columnName = val columnName =
if (deliveryReceipt) DELIVERY_RECEIPT_COUNT else READ_RECEIPT_COUNT if (deliveryReceipt) DELIVERY_RECEIPT_COUNT else READ_RECEIPT_COUNT
if (ourAddress.equals(theirAddress) || theirAddress.isGroup) { if (ourAddress.equals(theirAddress) || theirAddress.isGroupOrCommunity) {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)) val id = cursor.getLong(cursor.getColumnIndexOrThrow(ID))
val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)) val threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))
val status = val status =
@ -779,7 +779,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
contentValues, contentValues,
insertListener, insertListener,
) )
if (message.recipient.address.isGroup) { if (message.recipient.address.isGroupOrCommunity) {
val members = get(context).groupDatabase() val members = get(context).groupDatabase()
.getGroupMembers(message.recipient.address.toGroupString(), false) .getGroupMembers(message.recipient.address.toGroupString(), false)
val receiptDatabase = get(context).groupReceiptDatabase() val receiptDatabase = get(context).groupReceiptDatabase()

View File

@ -67,7 +67,6 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.recipients.Recipient.DisappearingState import org.session.libsession.utilities.recipients.Recipient.DisappearingState
import org.session.libsession.utilities.recipients.MessageType import org.session.libsession.utilities.recipients.MessageType
import org.session.libsession.utilities.recipients.getType import org.session.libsession.utilities.recipients.getType
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.crypto.ecc.DjbECPublicKey
import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.crypto.ecc.ECKeyPair
import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceAttachmentPointer
@ -134,73 +133,73 @@ open class Storage @Inject constructor(
val localUserAddress = getUserPublicKey() ?: return val localUserAddress = getUserPublicKey() ?: return
if (!getRecipientApproved(address) && localUserAddress != address.serialize()) return // don't store unapproved / message requests if (!getRecipientApproved(address) && localUserAddress != address.serialize()) return // don't store unapproved / message requests
if (address.isGroup) { when {
when { address.isLegacyGroup -> {
address.isLegacyClosedGroup -> { val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
val accountId = GroupUtil.doubleDecodeGroupId(address.serialize()) val closedGroup = getGroup(address.toGroupString())
val closedGroup = getGroup(address.toGroupString()) if (closedGroup != null && closedGroup.isActive) {
if (closedGroup != null && closedGroup.isActive) {
configFactory.withMutableUserConfigs { configs ->
val legacyGroup = configs.userGroups.getOrConstructLegacyGroupInfo(accountId)
configs.userGroups.set(legacyGroup)
val newVolatileParams = configs.convoInfoVolatile.getOrConstructLegacyGroup(accountId).copy(
lastRead = clock.currentTimeMills(),
)
configs.convoInfoVolatile.set(newVolatileParams)
}
}
}
address.isClosedGroupV2 -> {
configFactory.withMutableUserConfigs { configs -> configFactory.withMutableUserConfigs { configs ->
val accountId = address.serialize() val legacyGroup = configs.userGroups.getOrConstructLegacyGroupInfo(accountId)
configs.userGroups.getClosedGroup(accountId) configs.userGroups.set(legacyGroup)
?: return@withMutableUserConfigs Log.d("Closed group doesn't exist locally", NullPointerException()) val newVolatileParams = configs.convoInfoVolatile.getOrConstructLegacyGroup(accountId).copy(
lastRead = clock.currentTimeMills(),
configs.convoInfoVolatile.getOrConstructClosedGroup(accountId) )
configs.convoInfoVolatile.set(newVolatileParams)
} }
} }
address.isCommunity -> {
// these should be added on the group join / group info fetch
Log.w("Loki", "Thread created called for open group address, not adding any extra information")
}
} }
} else if (address.isContact) { address.isGroupV2 -> {
// non-standard contact prefixes: 15, 00 etc shouldn't be stored in config
if (AccountId(address.serialize()).prefix != IdPrefix.STANDARD) return
// don't update our own address into the contacts DB
if (getUserPublicKey() != address.serialize()) {
configFactory.withMutableUserConfigs { configs -> configFactory.withMutableUserConfigs { configs ->
configs.contacts.upsertContact(address.serialize()) { val accountId = address.serialize()
priority = PRIORITY_VISIBLE configs.userGroups.getClosedGroup(accountId)
?: return@withMutableUserConfigs Log.d("Closed group doesn't exist locally", NullPointerException())
configs.convoInfoVolatile.getOrConstructClosedGroup(accountId)
}
}
address.isCommunity -> {
// these should be added on the group join / group info fetch
Log.w("Loki", "Thread created called for open group address, not adding any extra information")
}
address.isContact -> {
// non-standard contact prefixes: 15, 00 etc shouldn't be stored in config
if (AccountId(address.serialize()).prefix != IdPrefix.STANDARD) return
// don't update our own address into the contacts DB
if (getUserPublicKey() != address.serialize()) {
configFactory.withMutableUserConfigs { configs ->
configs.contacts.upsertContact(address.serialize()) {
priority = PRIORITY_VISIBLE
}
} }
} else {
configFactory.withMutableUserConfigs { configs ->
configs.userProfile.setNtsPriority(PRIORITY_VISIBLE)
}
threadDatabase.setHasSent(threadId, true)
} }
} else {
configFactory.withMutableUserConfigs { configs -> configFactory.withMutableUserConfigs { configs ->
configs.userProfile.setNtsPriority(PRIORITY_VISIBLE) configs.convoInfoVolatile.getOrConstructOneToOne(address.serialize())
} }
threadDatabase.setHasSent(threadId, true)
}
configFactory.withMutableUserConfigs { configs ->
configs.convoInfoVolatile.getOrConstructOneToOne(address.serialize())
} }
} }
} }
override fun threadDeleted(address: Address, threadId: Long) { override fun threadDeleted(address: Address, threadId: Long) {
configFactory.withMutableUserConfigs { configs -> configFactory.withMutableUserConfigs { configs ->
if (address.isGroup) { if (address.isGroupOrCommunity) {
if (address.isLegacyClosedGroup) { if (address.isLegacyGroup) {
val accountId = GroupUtil.doubleDecodeGroupId(address.serialize()) val accountId = GroupUtil.doubleDecodeGroupId(address.serialize())
configs.convoInfoVolatile.eraseLegacyClosedGroup(accountId) configs.convoInfoVolatile.eraseLegacyClosedGroup(accountId)
configs.userGroups.eraseLegacyGroup(accountId) configs.userGroups.eraseLegacyGroup(accountId)
} else if (address.isCommunity) { } else if (address.isCommunity) {
// these should be removed in the group leave / handling new configs // these should be removed in the group leave / handling new configs
Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere") Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere")
} else if (address.isClosedGroupV2) { } else if (address.isGroupV2) {
Log.w("Loki", "Thread delete called for closed group address, expecting to be handled elsewhere") Log.w("Loki", "Thread delete called for closed group address, expecting to be handled elsewhere")
} }
} else { } else {
@ -327,17 +326,17 @@ open class Storage @Inject constructor(
getRecipientForThread(threadId)?.let { recipient -> getRecipientForThread(threadId)?.let { recipient ->
val currentLastRead = threadDb.getLastSeenAndHasSent(threadId).first() val currentLastRead = threadDb.getLastSeenAndHasSent(threadId).first()
// don't set the last read in the volatile if we didn't set it in the DB // don't set the last read in the volatile if we didn't set it in the DB
if (!threadDb.markAllAsRead(threadId, recipient.isGroupRecipient, lastSeenTime, force) && !force) return if (!threadDb.markAllAsRead(threadId, recipient.isGroupOrCommunityRecipient, lastSeenTime, force) && !force) return
// don't process configs for inbox recipients // don't process configs for inbox recipients
if (recipient.isOpenGroupInboxRecipient) return if (recipient.isCommunityInboxRecipient) return
configFactory.withMutableUserConfigs { configs -> configFactory.withMutableUserConfigs { configs ->
val config = configs.convoInfoVolatile val config = configs.convoInfoVolatile
val convo = when { val convo = when {
// recipient closed group // recipient closed group
recipient.isLegacyClosedGroupRecipient -> config.getOrConstructLegacyGroup(GroupUtil.doubleDecodeGroupId(recipient.address.serialize())) recipient.isLegacyGroupRecipient -> config.getOrConstructLegacyGroup(GroupUtil.doubleDecodeGroupId(recipient.address.serialize()))
recipient.isClosedGroupV2Recipient -> config.getOrConstructClosedGroup(recipient.address.serialize()) recipient.isGroupV2Recipient -> config.getOrConstructClosedGroup(recipient.address.serialize())
// recipient is open group // recipient is open group
recipient.isCommunityRecipient -> { recipient.isCommunityRecipient -> {
val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return@withMutableUserConfigs val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return@withMutableUserConfigs
@ -409,7 +408,7 @@ open class Storage @Inject constructor(
senderAddress senderAddress
} }
val targetRecipient = Recipient.from(context, targetAddress, false) val targetRecipient = Recipient.from(context, targetAddress, false)
if (!targetRecipient.isGroupRecipient) { if (!targetRecipient.isGroupOrCommunityRecipient) {
if (isUserSender || isUserBlindedSender) { if (isUserSender || isUserBlindedSender) {
setRecipientApproved(targetRecipient, true) setRecipientApproved(targetRecipient, true)
} else { } else {
@ -1390,9 +1389,9 @@ open class Storage @Inject constructor(
configs.contacts.upsertContact(threadRecipient.address.serialize()) { configs.contacts.upsertContact(threadRecipient.address.serialize()) {
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
} }
} else if (threadRecipient.isGroupRecipient) { } else if (threadRecipient.isGroupOrCommunityRecipient) {
when { when {
threadRecipient.isLegacyClosedGroupRecipient -> { threadRecipient.isLegacyGroupRecipient -> {
threadRecipient.address.serialize() threadRecipient.address.serialize()
.let(GroupUtil::doubleDecodeGroupId) .let(GroupUtil::doubleDecodeGroupId)
.let(configs.userGroups::getOrConstructLegacyGroupInfo) .let(configs.userGroups::getOrConstructLegacyGroupInfo)
@ -1400,7 +1399,7 @@ open class Storage @Inject constructor(
.let(configs.userGroups::set) .let(configs.userGroups::set)
} }
threadRecipient.isClosedGroupV2Recipient -> { threadRecipient.isGroupV2Recipient -> {
val newGroupInfo = configs.userGroups val newGroupInfo = configs.userGroups
.getOrConstructClosedGroup(threadRecipient.address.serialize()) .getOrConstructClosedGroup(threadRecipient.address.serialize())
.copy(priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE) .copy(priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE)
@ -1586,8 +1585,8 @@ open class Storage @Inject constructor(
val recipient = reader.current.recipient val recipient = reader.current.recipient
val address = recipient.address.serialize() val address = recipient.address.serialize()
val blindedId = when { val blindedId = when {
recipient.isGroupRecipient -> null recipient.isGroupOrCommunityRecipient -> null
recipient.isOpenGroupInboxRecipient -> GroupUtil.getDecodedOpenGroupInboxAccountId(address) recipient.isCommunityInboxRecipient -> GroupUtil.getDecodedOpenGroupInboxAccountId(address)
else -> address.takeIf { AccountId(it).prefix == IdPrefix.BLINDED } else -> address.takeIf { AccountId(it).prefix == IdPrefix.BLINDED }
} ?: continue } ?: continue
mappingDb.getBlindedIdMapping(blindedId).firstOrNull()?.let { mappingDb.getBlindedIdMapping(blindedId).firstOrNull()?.let {
@ -1668,7 +1667,7 @@ open class Storage @Inject constructor(
} }
override fun getRecipientApproved(address: Address): Boolean { override fun getRecipientApproved(address: Address): Boolean {
return address.isClosedGroupV2 || recipientDatabase.getApproved(address) return address.isGroupV2 || recipientDatabase.getApproved(address)
} }
override fun setRecipientApproved(recipient: Recipient, approved: Boolean) { override fun setRecipientApproved(recipient: Recipient, approved: Boolean) {
@ -1864,14 +1863,14 @@ open class Storage @Inject constructor(
recipient.address.serialize().takeIf { it.startsWith(IdPrefix.STANDARD.value) } recipient.address.serialize().takeIf { it.startsWith(IdPrefix.STANDARD.value) }
?.let { configFactory.withUserConfigs { configs -> configs.contacts.get(it)?.expiryMode } } ?.let { configFactory.withUserConfigs { configs -> configs.contacts.get(it)?.expiryMode } }
} }
recipient.isClosedGroupV2Recipient -> { recipient.isGroupV2Recipient -> {
configFactory.withGroupConfigs(AccountId(recipient.address.serialize())) { configs -> configFactory.withGroupConfigs(AccountId(recipient.address.serialize())) { configs ->
configs.groupInfo.getExpiryTimer() configs.groupInfo.getExpiryTimer()
}.let { }.let {
if (it == 0L) ExpiryMode.NONE else ExpiryMode.AfterSend(it) if (it == 0L) ExpiryMode.NONE else ExpiryMode.AfterSend(it)
} }
} }
recipient.isLegacyClosedGroupRecipient -> { recipient.isLegacyGroupRecipient -> {
// read it from group config if exists // read it from group config if exists
GroupUtil.doubleDecodeGroupId(recipient.address.serialize()) GroupUtil.doubleDecodeGroupId(recipient.address.serialize())
.let { id -> configFactory.withUserConfigs { it.userGroups.getLegacyGroupInfo(id) } } .let { id -> configFactory.withUserConfigs { it.userGroups.getLegacyGroupInfo(id) } }
@ -1899,7 +1898,7 @@ open class Storage @Inject constructor(
lokiAPIDatabase.setLastLegacySenderAddress(recipient.address.serialize(), null) lokiAPIDatabase.setLastLegacySenderAddress(recipient.address.serialize(), null)
} }
if (recipient.isLegacyClosedGroupRecipient) { if (recipient.isLegacyGroupRecipient) {
val groupPublicKey = GroupUtil.addressToGroupAccountId(recipient.address) val groupPublicKey = GroupUtil.addressToGroupAccountId(recipient.address)
configFactory.withMutableUserConfigs { configFactory.withMutableUserConfigs {
@ -1907,7 +1906,7 @@ open class Storage @Inject constructor(
?.copy(disappearingTimer = expiryMode.expirySeconds) ?: return@withMutableUserConfigs ?.copy(disappearingTimer = expiryMode.expirySeconds) ?: return@withMutableUserConfigs
it.userGroups.set(groupInfo) it.userGroups.set(groupInfo)
} }
} else if (recipient.isClosedGroupV2Recipient) { } else if (recipient.isGroupV2Recipient) {
val groupSessionId = AccountId(recipient.address.serialize()) val groupSessionId = AccountId(recipient.address.serialize())
configFactory.withMutableGroupConfigs(groupSessionId) { configs -> configFactory.withMutableGroupConfigs(groupSessionId) { configs ->
configs.groupInfo.setExpiryTimer(expiryMode.expirySeconds) configs.groupInfo.setExpiryTimer(expiryMode.expirySeconds)

View File

@ -665,7 +665,7 @@ public class ThreadDatabase extends Database {
threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else { } else {
DatabaseComponent.get(context).recipientDatabase().setProfileSharing(recipient, true); DatabaseComponent.get(context).recipientDatabase().setProfileSharing(recipient, true);
threadId = createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); threadId = createThreadForRecipient(recipient.getAddress(), recipient.isGroupOrCommunityRecipient(), distributionType);
created = true; created = true;
} }
if (created && updateListener != null) { if (created && updateListener != null) {

View File

@ -131,7 +131,7 @@ public abstract class MessageRecord extends DisplayRecord {
); );
} else if (isExpirationTimerUpdate()) { } else if (isExpirationTimerUpdate()) {
int seconds = (int) (getExpiresIn() / 1000); int seconds = (int) (getExpiresIn() / 1000);
boolean isGroup = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(getThreadId()).isGroupRecipient(); boolean isGroup = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(getThreadId()).isGroupOrCommunityRecipient();
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, isGroup, getIndividualRecipient().getAddress().serialize(), isOutgoing(), getTimestamp(), expireStarted)); return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, isGroup, getIndividualRecipient().getAddress().serialize(), isOutgoing(), getTimestamp(), expireStarted));
} else if (isDataExtractionNotification()) { } else if (isDataExtractionNotification()) {
if (isScreenshotNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize()))); if (isScreenshotNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));

View File

@ -545,18 +545,18 @@ private fun MutableConversationVolatileConfig.initFrom(storage: StorageProtocol,
val (base, room, pubKey) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: continue val (base, room, pubKey) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: continue
getOrConstructCommunity(base, room, pubKey) getOrConstructCommunity(base, room, pubKey)
} }
recipient.isClosedGroupV2Recipient -> { recipient.isGroupV2Recipient -> {
// It's probably safe to assume there will never be a case where new closed groups will ever be there before a dump is created... // It's probably safe to assume there will never be a case where new closed groups will ever be there before a dump is created...
// but just in case... // but just in case...
getOrConstructClosedGroup(recipient.address.serialize()) getOrConstructClosedGroup(recipient.address.serialize())
} }
recipient.isLegacyClosedGroupRecipient -> { recipient.isLegacyGroupRecipient -> {
val groupPublicKey = GroupUtil.doubleDecodeGroupId(recipient.address.serialize()) val groupPublicKey = GroupUtil.doubleDecodeGroupId(recipient.address.serialize())
getOrConstructLegacyGroup(groupPublicKey) getOrConstructLegacyGroup(groupPublicKey)
} }
recipient.isContactRecipient -> { recipient.isContactRecipient -> {
if (recipient.isLocalNumber) null // this is handled by the user profile NTS data if (recipient.isLocalNumber) null // this is handled by the user profile NTS data
else if (recipient.isOpenGroupInboxRecipient) null // specifically exclude else if (recipient.isCommunityInboxRecipient) null // specifically exclude
else if (!recipient.address.serialize().startsWith(IdPrefix.STANDARD.value)) null else if (!recipient.address.serialize().startsWith(IdPrefix.STANDARD.value)) null
else getOrConstructOneToOne(recipient.address.serialize()) else getOrConstructOneToOne(recipient.address.serialize())
} }

View File

@ -68,7 +68,7 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto
if (!this::thread.isInitialized) { return dismiss() } if (!this::thread.isInitialized) { return dismiss() }
val recipient = thread.recipient val recipient = thread.recipient
val isCurrentUserInGroup = group?.members?.map { it.toString() }?.contains(publicKey) ?: false val isCurrentUserInGroup = group?.members?.map { it.toString() }?.contains(publicKey) ?: false
if (!recipient.isGroupRecipient && !recipient.isLocalNumber) { if (!recipient.isGroupOrCommunityRecipient && !recipient.isLocalNumber) {
binding.detailsTextView.visibility = View.VISIBLE binding.detailsTextView.visibility = View.VISIBLE
binding.unblockTextView.visibility = if (recipient.isBlocked) View.VISIBLE else View.GONE binding.unblockTextView.visibility = if (recipient.isBlocked) View.VISIBLE else View.GONE
binding.blockTextView.visibility = if (recipient.isBlocked) View.GONE else View.VISIBLE binding.blockTextView.visibility = if (recipient.isBlocked) View.GONE else View.VISIBLE
@ -78,7 +78,7 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto
} else { } else {
binding.detailsTextView.visibility = View.GONE binding.detailsTextView.visibility = View.GONE
} }
binding.copyConversationId.visibility = if (!recipient.isGroupRecipient && !recipient.isLocalNumber) View.VISIBLE else View.GONE binding.copyConversationId.visibility = if (!recipient.isGroupOrCommunityRecipient && !recipient.isLocalNumber) View.VISIBLE else View.GONE
binding.copyConversationId.setOnClickListener(this) binding.copyConversationId.setOnClickListener(this)
binding.copyCommunityUrl.visibility = if (recipient.isCommunityRecipient) View.VISIBLE else View.GONE binding.copyCommunityUrl.visibility = if (recipient.isCommunityRecipient) View.VISIBLE else View.GONE
binding.copyCommunityUrl.setOnClickListener(this) binding.copyCommunityUrl.setOnClickListener(this)
@ -86,18 +86,18 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto
binding.muteNotificationsTextView.isVisible = !recipient.isMuted && !recipient.isLocalNumber binding.muteNotificationsTextView.isVisible = !recipient.isMuted && !recipient.isLocalNumber
binding.unMuteNotificationsTextView.setOnClickListener(this) binding.unMuteNotificationsTextView.setOnClickListener(this)
binding.muteNotificationsTextView.setOnClickListener(this) binding.muteNotificationsTextView.setOnClickListener(this)
binding.notificationsTextView.isVisible = recipient.isGroupRecipient && !recipient.isMuted binding.notificationsTextView.isVisible = recipient.isGroupOrCommunityRecipient && !recipient.isMuted
binding.notificationsTextView.setOnClickListener(this) binding.notificationsTextView.setOnClickListener(this)
// delete // delete
binding.deleteTextView.apply { binding.deleteTextView.apply {
isVisible = recipient.isContactRecipient || (recipient.isGroupRecipient && !isCurrentUserInGroup) isVisible = recipient.isContactRecipient || (recipient.isGroupOrCommunityRecipient && !isCurrentUserInGroup)
setOnClickListener(this@ConversationOptionsBottomSheet) setOnClickListener(this@ConversationOptionsBottomSheet)
// the text and content description will change depending on the type // the text and content description will change depending on the type
when{ when{
// groups and communities // groups and communities
recipient.isGroupRecipient -> { recipient.isGroupOrCommunityRecipient -> {
text = context.getString(R.string.leave) text = context.getString(R.string.leave)
contentDescription = context.getString(R.string.AccessibilityId_leave) contentDescription = context.getString(R.string.AccessibilityId_leave)
} }
@ -115,7 +115,7 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto
} }
} }
} }
binding.leaveTextView.isVisible = recipient.isGroupRecipient && isCurrentUserInGroup binding.leaveTextView.isVisible = recipient.isGroupOrCommunityRecipient && isCurrentUserInGroup
binding.leaveTextView.setOnClickListener(this) binding.leaveTextView.setOnClickListener(this)
binding.markAllAsReadTextView.isVisible = thread.unreadCount > 0 || binding.markAllAsReadTextView.isVisible = thread.unreadCount > 0 ||

View File

@ -99,7 +99,7 @@ class ConversationView : LinearLayout {
binding.unreadCountIndicator.isVisible = (unreadCount != 0 && !thread.isRead) binding.unreadCountIndicator.isVisible = (unreadCount != 0 && !thread.isRead)
|| (configFactory.withUserConfigs { it.convoInfoVolatile.getConversationUnread(thread) }) || (configFactory.withUserConfigs { it.convoInfoVolatile.getConversationUnread(thread) })
binding.unreadMentionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize) binding.unreadMentionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
binding.unreadMentionIndicator.isVisible = (thread.unreadMentionCount != 0 && thread.recipient.address.isGroup) binding.unreadMentionIndicator.isVisible = (thread.unreadMentionCount != 0 && thread.recipient.address.isGroupOrCommunity)
val senderDisplayName = getTitle(thread.recipient) val senderDisplayName = getTitle(thread.recipient)
?: thread.recipient.address.toString() ?: thread.recipient.address.toString()
binding.conversationViewDisplayNameTextView.text = senderDisplayName binding.conversationViewDisplayNameTextView.text = senderDisplayName

View File

@ -442,7 +442,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
bottomSheet.onCopyConversationId = onCopyConversationId@{ bottomSheet.onCopyConversationId = onCopyConversationId@{
bottomSheet.dismiss() bottomSheet.dismiss()
if (!thread.recipient.isGroupRecipient && !thread.recipient.isLocalNumber) { if (!thread.recipient.isGroupOrCommunityRecipient && !thread.recipient.isLocalNumber) {
val clip = ClipData.newPlainText("Account ID", thread.recipient.address.toString()) val clip = ClipData.newPlainText("Account ID", thread.recipient.address.toString())
val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
manager.setPrimaryClip(clip) manager.setPrimaryClip(clip)
@ -583,7 +583,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
val threadID = thread.threadId val threadID = thread.threadId
val recipient = thread.recipient val recipient = thread.recipient
if (recipient.isClosedGroupV2Recipient || recipient.isLegacyClosedGroupRecipient) { if (recipient.isGroupV2Recipient || recipient.isLegacyGroupRecipient) {
ConversationMenuHelper.leaveClosedGroup( ConversationMenuHelper.leaveClosedGroup(
context = this, context = this,
thread = recipient, thread = recipient,
@ -601,7 +601,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
var positiveButtonId: Int = R.string.yes var positiveButtonId: Int = R.string.yes
var negativeButtonId: Int = R.string.no var negativeButtonId: Int = R.string.no
if (recipient.isGroupRecipient) { if (recipient.isGroupOrCommunityRecipient) {
val group = groupDatabase.getGroup(recipient.address.toString()).orNull() val group = groupDatabase.getGroup(recipient.address.toString()).orNull()
// If you are an admin of this group you can delete it // If you are an admin of this group you can delete it
@ -666,7 +666,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) ApplicationContext.getInstance(context).messageNotifier.updateNotification(context)
// Notify the user // Notify the user
val toastMessage = if (recipient.isGroupRecipient) R.string.groupMemberYouLeft else R.string.conversationsDeleted val toastMessage = if (recipient.isGroupOrCommunityRecipient) R.string.groupMemberYouLeft else R.string.conversationsDeleted
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show() Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show()
} }
} }

View File

@ -59,7 +59,7 @@ class UserDetailsBottomSheet: BottomSheetDialogFragment() {
profilePictureView.update(recipient) profilePictureView.update(recipient)
nameTextViewContainer.visibility = View.VISIBLE nameTextViewContainer.visibility = View.VISIBLE
nameTextViewContainer.setOnClickListener { nameTextViewContainer.setOnClickListener {
if (recipient.isOpenGroupInboxRecipient || recipient.isOpenGroupOutboxRecipient) return@setOnClickListener if (recipient.isCommunityInboxRecipient || recipient.isCommunityOutboxRecipient) return@setOnClickListener
nameTextViewContainer.visibility = View.INVISIBLE nameTextViewContainer.visibility = View.INVISIBLE
nameEditTextContainer.visibility = View.VISIBLE nameEditTextContainer.visibility = View.VISIBLE
nicknameEditText.text = null nicknameEditText.text = null
@ -87,12 +87,12 @@ class UserDetailsBottomSheet: BottomSheetDialogFragment() {
nameTextView.text = recipient.name ?: publicKey // Uses the Contact API internally nameTextView.text = recipient.name ?: publicKey // Uses the Contact API internally
nameEditIcon.isVisible = threadRecipient.isContactRecipient nameEditIcon.isVisible = threadRecipient.isContactRecipient
&& !threadRecipient.isOpenGroupInboxRecipient && !threadRecipient.isCommunityInboxRecipient
&& !threadRecipient.isOpenGroupOutboxRecipient && !threadRecipient.isCommunityOutboxRecipient
publicKeyTextView.isVisible = !threadRecipient.isCommunityRecipient publicKeyTextView.isVisible = !threadRecipient.isCommunityRecipient
&& !threadRecipient.isOpenGroupInboxRecipient && !threadRecipient.isCommunityInboxRecipient
&& !threadRecipient.isOpenGroupOutboxRecipient && !threadRecipient.isCommunityOutboxRecipient
messageButton.isVisible = !threadRecipient.isCommunityRecipient || IdPrefix.fromValue(publicKey)?.isBlinded() == true messageButton.isVisible = !threadRecipient.isCommunityRecipient || IdPrefix.fromValue(publicKey)?.isBlinded() == true
publicKeyTextView.text = publicKey publicKeyTextView.text = publicKey
publicKeyTextView.setOnLongClickListener { publicKeyTextView.setOnLongClickListener {

View File

@ -282,7 +282,7 @@ class MediaOverviewViewModel(
// in groups/communities) // in groups/communities)
if (selectedMedia.any { !it.mediaRecord.isOutgoing } && if (selectedMedia.any { !it.mediaRecord.isOutgoing } &&
successCount > 0 && successCount > 0 &&
!address.isGroup) { !address.isGroupOrCommunity) {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val timestamp = SnodeAPI.nowWithOffset val timestamp = SnodeAPI.nowWithOffset
val kind = DataExtractionNotification.Kind.MediaSaved(timestamp) val kind = DataExtractionNotification.Kind.MediaSaved(timestamp)

View File

@ -32,7 +32,7 @@ class MessageRequestsAdapter(
view.setOnClickListener { view.thread?.let { listener.onConversationClick(it) } } view.setOnClickListener { view.thread?.let { listener.onConversationClick(it) } }
view.setOnLongClickListener { view.setOnLongClickListener {
view.thread?.let { thread -> view.thread?.let { thread ->
showPopupMenu(view, thread.recipient.isGroupRecipient, thread.invitingAdminId) showPopupMenu(view, thread.recipient.isGroupOrCommunityRecipient, thread.invitingAdminId)
} }
true true
} }
@ -52,7 +52,7 @@ class MessageRequestsAdapter(
private fun showPopupMenu(view: MessageRequestView, groupRecipient: Boolean, invitingAdmin: String?) { private fun showPopupMenu(view: MessageRequestView, groupRecipient: Boolean, invitingAdmin: String?) {
val popupMenu = PopupMenu(ContextThemeWrapper(context, R.style.PopupMenu_MessageRequests), view) val popupMenu = PopupMenu(ContextThemeWrapper(context, R.style.PopupMenu_MessageRequests), view)
// still show the block option if we have an inviting admin for the group // still show the block option if we have an inviting admin for the group
if ((groupRecipient && invitingAdmin == null) || view.thread!!.recipient.isOpenGroupInboxRecipient) { if ((groupRecipient && invitingAdmin == null) || view.thread!!.recipient.isCommunityInboxRecipient) {
popupMenu.menuInflater.inflate(R.menu.menu_group_request, popupMenu.menu) popupMenu.menuInflater.inflate(R.menu.menu_group_request, popupMenu.menu)
} else { } else {
popupMenu.menuInflater.inflate(R.menu.menu_message_request, popupMenu.menu) popupMenu.menuInflater.inflate(R.menu.menu_message_request, popupMenu.menu)

View File

@ -93,7 +93,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
long expiresInMillis = expiryMode == null ? 0 : expiryMode.getExpiryMillis(); long expiresInMillis = expiryMode == null ? 0 : expiryMode.getExpiryMillis();
long expireStartedAt = expiryMode instanceof ExpiryMode.AfterSend ? message.getSentTimestamp() : 0L; long expireStartedAt = expiryMode instanceof ExpiryMode.AfterSend ? message.getSentTimestamp() : 0L;
if (recipient.isGroupRecipient()) { if (recipient.isGroupOrCommunityRecipient()) {
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null, expiresInMillis, 0); OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null, expiresInMillis, 0);
try { try {

View File

@ -28,8 +28,6 @@ import android.database.Cursor
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import android.widget.Toast
import androidx.camera.core.impl.utils.ContextUtil.getApplicationContext
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -69,8 +67,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.ReactionRecord import org.thoughtcrime.securesms.database.model.ReactionRecord
import org.thoughtcrime.securesms.dependencies.DatabaseComponent.Companion.get import org.thoughtcrime.securesms.dependencies.DatabaseComponent.Companion.get
import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.SlideDeck
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.preferences.ShareLogsDialog
import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.SessionMetaProtocol.canUserReplyToNotification import org.thoughtcrime.securesms.util.SessionMetaProtocol.canUserReplyToNotification
import org.thoughtcrime.securesms.util.SpanUtil import org.thoughtcrime.securesms.util.SpanUtil
@ -171,7 +167,7 @@ class DefaultMessageNotifier : MessageNotifier {
val threads = get(context).threadDatabase() val threads = get(context).threadDatabase()
val recipient = threads.getRecipientForThreadId(threadId) val recipient = threads.getRecipientForThreadId(threadId)
if (recipient != null && !recipient.isGroupRecipient && threads.getMessageCount(threadId) == 1 && if (recipient != null && !recipient.isGroupOrCommunityRecipient && threads.getMessageCount(threadId) == 1 &&
!(recipient.isApproved || threads.getLastSeenAndHasSent(threadId).second()) !(recipient.isApproved || threads.getLastSeenAndHasSent(threadId).second())
) { ) {
removeHasHiddenMessageRequests(context) removeHasHiddenMessageRequests(context)
@ -485,7 +481,7 @@ class DefaultMessageNotifier : MessageNotifier {
if (threadId != -1L) { if (threadId != -1L) {
threadRecipients = threadDatabase.getRecipientForThreadId(threadId) threadRecipients = threadDatabase.getRecipientForThreadId(threadId)
messageRequest = threadRecipients != null && !threadRecipients.isGroupRecipient && messageRequest = threadRecipients != null && !threadRecipients.isGroupOrCommunityRecipient &&
!threadRecipients.isApproved && !threadDatabase.getLastSeenAndHasSent(threadId).second() !threadRecipients.isApproved && !threadDatabase.getLastSeenAndHasSent(threadId).second()
if (messageRequest && (threadDatabase.getMessageCount(threadId) > 1 || !hasHiddenMessageRequests(context))) { if (messageRequest && (threadDatabase.getMessageCount(threadId) > 1 || !hasHiddenMessageRequests(context))) {
continue continue
@ -558,7 +554,7 @@ class DefaultMessageNotifier : MessageNotifier {
.findLast() .findLast()
if (lastReact.isPresent) { if (lastReact.isPresent) {
if (threadRecipients != null && !threadRecipients.isGroupRecipient) { if (threadRecipients != null && !threadRecipients.isGroupOrCommunityRecipient) {
val reaction = lastReact.get() val reaction = lastReact.get()
val reactor = Recipient.from(context, fromSerialized(reaction.author), false) val reactor = Recipient.from(context, fromSerialized(reaction.author), false)
val emoji = Phrase.from(context, R.string.emojiReactsNotification).put(EMOJI_KEY, reaction.emoji).format().toString() val emoji = Phrase.from(context, R.string.emojiReactsNotification).put(EMOJI_KEY, reaction.emoji).format().toString()

View File

@ -73,7 +73,7 @@ class MarkReadReceiver : BroadcastReceiver() {
.filter { it.expiryType == ExpiryType.AFTER_READ } .filter { it.expiryType == ExpiryType.AFTER_READ }
.map { it.syncMessageId } .map { it.syncMessageId }
.filter { mmsSmsDatabase.getMessageForTimestamp(it.timetamp)?.run { .filter { mmsSmsDatabase.getMessageForTimestamp(it.timetamp)?.run {
isExpirationTimerUpdate && threadDb.getRecipientForThreadId(threadId)?.isGroupRecipient == true } == false isExpirationTimerUpdate && threadDb.getRecipientForThreadId(threadId)?.isGroupOrCommunityRecipient == true } == false
} }
.forEach { messageExpirationManager.startDisappearAfterRead(it.timetamp, it.address.serialize()) } .forEach { messageExpirationManager.startDisappearAfterRead(it.timetamp, it.address.serialize()) }

View File

@ -40,7 +40,7 @@ class MultipleRecipientNotificationBuilder(context: Context, privacy: Notificati
fun setMostRecentSender(recipient: Recipient, threadRecipient: Recipient) { fun setMostRecentSender(recipient: Recipient, threadRecipient: Recipient) {
var displayName = recipient.toShortString() var displayName = recipient.toShortString()
if (threadRecipient.isGroupRecipient) { if (threadRecipient.isGroupOrCommunityRecipient) {
displayName = getGroupDisplayName(recipient, threadRecipient.isCommunityRecipient) displayName = getGroupDisplayName(recipient, threadRecipient.isCommunityRecipient)
} }
if (privacy.isDisplayContact) { if (privacy.isDisplayContact) {
@ -69,7 +69,7 @@ class MultipleRecipientNotificationBuilder(context: Context, privacy: Notificati
fun addMessageBody(sender: Recipient, threadRecipient: Recipient, body: CharSequence?) { fun addMessageBody(sender: Recipient, threadRecipient: Recipient, body: CharSequence?) {
var displayName = sender.toShortString() var displayName = sender.toShortString()
if (threadRecipient.isGroupRecipient) { if (threadRecipient.isGroupOrCommunityRecipient) {
displayName = getGroupDisplayName(sender, threadRecipient.isCommunityRecipient) displayName = getGroupDisplayName(sender, threadRecipient.isCommunityRecipient)
} }
if (privacy.isDisplayMessage) { if (privacy.isDisplayMessage) {

View File

@ -11,7 +11,7 @@ public enum ReplyMethod {
SecureMessage; SecureMessage;
public static @NonNull ReplyMethod forRecipient(Context context, Recipient recipient) { public static @NonNull ReplyMethod forRecipient(Context context, Recipient recipient) {
if (recipient.isGroupRecipient()) { if (recipient.isGroupOrCommunityRecipient()) {
return ReplyMethod.GroupMessage; return ReplyMethod.GroupMessage;
} }
return ReplyMethod.SecureMessage; return ReplyMethod.SecureMessage;

View File

@ -29,7 +29,6 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.session.libsession.avatars.ContactPhoto; import org.session.libsession.avatars.ContactPhoto;
import org.session.libsession.messaging.contacts.Contact; import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.NotificationPrivacyPreference;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
@ -119,7 +118,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && threadRecipient.isGroupRecipient()) { if (privacy.isDisplayContact() && threadRecipient.isGroupOrCommunityRecipient()) {
String displayName = getGroupDisplayName(individualRecipient, threadRecipient.isCommunityRecipient()); String displayName = getGroupDisplayName(individualRecipient, threadRecipient.isCommunityRecipient());
stringBuilder.append(Util.getBoldedString(displayName + ": ")); stringBuilder.append(Util.getBoldedString(displayName + ": "));
} }
@ -207,7 +206,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
{ {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (privacy.isDisplayContact() && threadRecipient.isGroupRecipient()) { if (privacy.isDisplayContact() && threadRecipient.isGroupOrCommunityRecipient()) {
String displayName = getGroupDisplayName(individualRecipient, threadRecipient.isCommunityRecipient()); String displayName = getGroupDisplayName(individualRecipient, threadRecipient.isCommunityRecipient());
stringBuilder.append(Util.getBoldedString(displayName + ": ")); stringBuilder.append(Util.getBoldedString(displayName + ": "));
} }

View File

@ -28,7 +28,6 @@ import org.session.libsession.snode.utilities.await
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.getClosedGroup
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.AccountId
import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DatabaseContentProviders
@ -113,7 +112,7 @@ class DefaultConversationRepository @Inject constructor(
} }
override fun maybeGetBlindedRecipient(recipient: Recipient): Recipient? { override fun maybeGetBlindedRecipient(recipient: Recipient): Recipient? {
if (!recipient.isOpenGroupInboxRecipient) return null if (!recipient.isCommunityInboxRecipient) return null
return Recipient.from( return Recipient.from(
context, context,
Address.fromSerialized(GroupUtil.getDecodedOpenGroupInboxAccountId(recipient.address.serialize())), Address.fromSerialized(GroupUtil.getDecodedOpenGroupInboxAccountId(recipient.address.serialize())),
@ -173,7 +172,7 @@ class DefaultConversationRepository @Inject constructor(
override fun isGroupReadOnly(recipient: Recipient): Boolean { override fun isGroupReadOnly(recipient: Recipient): Boolean {
// We only care about group v2 recipient // We only care about group v2 recipient
if (!recipient.isClosedGroupV2Recipient) { if (!recipient.isGroupV2Recipient) {
return false return false
} }
@ -304,7 +303,7 @@ class DefaultConversationRepository @Inject constructor(
recipient: Recipient, recipient: Recipient,
messages: Set<MessageRecord> messages: Set<MessageRecord>
) { ) {
if (recipient.isLegacyClosedGroupRecipient) { if (recipient.isLegacyGroupRecipient) {
val publicKey = recipient.address val publicKey = recipient.address
messages.forEach { message -> messages.forEach { message ->
@ -343,7 +342,7 @@ class DefaultConversationRepository @Inject constructor(
} }
private fun shouldSendUnsendRequest(recipient: Recipient): Boolean { private fun shouldSendUnsendRequest(recipient: Recipient): Boolean {
return recipient.is1on1 || recipient.isLegacyClosedGroupRecipient return recipient.is1on1 || recipient.isLegacyGroupRecipient
} }
private fun buildUnsendRequest(message: MessageRecord): UnsendRequest { private fun buildUnsendRequest(message: MessageRecord): UnsendRequest {
@ -383,7 +382,7 @@ class DefaultConversationRepository @Inject constructor(
while (reader.next != null) { while (reader.next != null) {
deleteMessageRequest(reader.current) deleteMessageRequest(reader.current)
val recipient = reader.current.recipient val recipient = reader.current.recipient
if (block && !recipient.isClosedGroupV2Recipient) { if (block && !recipient.isGroupV2Recipient) {
setBlocked(reader.current.threadId, recipient, true) setBlocked(reader.current.threadId, recipient, true)
} }
} }
@ -394,7 +393,7 @@ class DefaultConversationRepository @Inject constructor(
override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient) = runCatching { override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient) = runCatching {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
storage.setRecipientApproved(recipient, true) storage.setRecipientApproved(recipient, true)
if (recipient.isClosedGroupV2Recipient) { if (recipient.isGroupV2Recipient) {
groupManager.respondToInvitation( groupManager.respondToInvitation(
AccountId(recipient.address.serialize()), AccountId(recipient.address.serialize()),
approved = true approved = true
@ -418,7 +417,7 @@ class DefaultConversationRepository @Inject constructor(
override suspend fun declineMessageRequest(threadId: Long, recipient: Recipient): Result<Unit> = runCatching { override suspend fun declineMessageRequest(threadId: Long, recipient: Recipient): Result<Unit> = runCatching {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
sessionJobDb.cancelPendingMessageSendJobs(threadId) sessionJobDb.cancelPendingMessageSendJobs(threadId)
if (recipient.isClosedGroupV2Recipient) { if (recipient.isGroupV2Recipient) {
groupManager.respondToInvitation( groupManager.respondToInvitation(
AccountId(recipient.address.serialize()), AccountId(recipient.address.serialize()),
approved = false approved = false

View File

@ -40,7 +40,7 @@ object SessionMetaProtocol {
@JvmStatic @JvmStatic
fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean { fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean {
if (address.isGroup) { return false } if (address.isGroupOrCommunity) { return false }
val hasBody = message.body.isPresent && message.body.get().isNotEmpty() val hasBody = message.body.isPresent && message.body.get().isNotEmpty()
val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty() val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty()
val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty() val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty()
@ -49,11 +49,11 @@ object SessionMetaProtocol {
@JvmStatic @JvmStatic
fun shouldSendReadReceipt(recipient: Recipient): Boolean { fun shouldSendReadReceipt(recipient: Recipient): Boolean {
return !recipient.isGroupRecipient && recipient.isApproved && !recipient.isBlocked return !recipient.isGroupOrCommunityRecipient && recipient.isApproved && !recipient.isBlocked
} }
@JvmStatic @JvmStatic
fun shouldSendTypingIndicator(recipient: Recipient): Boolean { fun shouldSendTypingIndicator(recipient: Recipient): Boolean {
return !recipient.isGroupRecipient && recipient.isApproved && !recipient.isBlocked return !recipient.isGroupOrCommunityRecipient && recipient.isApproved && !recipient.isBlocked
} }
} }

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.util package org.thoughtcrime.securesms.util
import network.loki.messenger.libsession_util.ConversationVolatileConfig
import network.loki.messenger.libsession_util.ReadableConversationVolatileConfig import network.loki.messenger.libsession_util.ReadableConversationVolatileConfig
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
@ -10,12 +9,12 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord
fun ReadableConversationVolatileConfig.getConversationUnread(thread: ThreadRecord): Boolean { fun ReadableConversationVolatileConfig.getConversationUnread(thread: ThreadRecord): Boolean {
val recipient = thread.recipient val recipient = thread.recipient
if (recipient.isContactRecipient if (recipient.isContactRecipient
&& recipient.isOpenGroupInboxRecipient && recipient.isCommunityInboxRecipient
&& recipient.address.serialize().startsWith(IdPrefix.STANDARD.value)) { && recipient.address.serialize().startsWith(IdPrefix.STANDARD.value)) {
return getOneToOne(recipient.address.serialize())?.unread == true return getOneToOne(recipient.address.serialize())?.unread == true
} else if (recipient.isClosedGroupV2Recipient) { } else if (recipient.isGroupV2Recipient) {
return getClosedGroup(recipient.address.serialize())?.unread == true return getClosedGroup(recipient.address.serialize())?.unread == true
} else if (recipient.isLegacyClosedGroupRecipient) { } else if (recipient.isLegacyGroupRecipient) {
return getLegacyClosedGroup(GroupUtil.doubleDecodeGroupId(recipient.address.toGroupString()))?.unread == true return getLegacyClosedGroup(GroupUtil.doubleDecodeGroupId(recipient.address.toGroupString()))?.unread == true
} else if (recipient.isCommunityRecipient) { } else if (recipient.isCommunityRecipient) {
val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(thread.threadId) ?: return false val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(thread.threadId) ?: return false

View File

@ -443,8 +443,8 @@ class DisappearingMessagesViewModelTest {
mockStuff(mode) mockStuff(mode)
whenever(recipient.address).thenReturn(GROUP_ADDRESS) whenever(recipient.address).thenReturn(GROUP_ADDRESS)
whenever(recipient.isGroupRecipient).thenReturn(true) whenever(recipient.isGroupOrCommunityRecipient).thenReturn(true)
whenever(recipient.isLegacyClosedGroupRecipient).thenReturn(true) whenever(recipient.isLegacyGroupRecipient).thenReturn(true)
whenever(groupDb.getGroup(any<String>())).thenReturn(Optional.of(groupRecord)) whenever(groupDb.getGroup(any<String>())).thenReturn(Optional.of(groupRecord))
whenever(groupRecord.admins).thenReturn( whenever(groupRecord.admins).thenReturn(
buildList { buildList {

View File

@ -4,7 +4,6 @@ import android.app.Application
import com.goterl.lazysodium.utils.KeyPair import com.goterl.lazysodium.utils.KeyPair
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import org.hamcrest.CoreMatchers.endsWith
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.CoreMatchers.nullValue import org.hamcrest.CoreMatchers.nullValue
@ -166,8 +165,8 @@ class ConversationViewModelTest: BaseViewModelTest() {
@Test @Test
fun `open group recipient should have no blinded recipient`() { fun `open group recipient should have no blinded recipient`() {
whenever(recipient.isCommunityRecipient).thenReturn(true) whenever(recipient.isCommunityRecipient).thenReturn(true)
whenever(recipient.isOpenGroupOutboxRecipient).thenReturn(false) whenever(recipient.isCommunityOutboxRecipient).thenReturn(false)
whenever(recipient.isOpenGroupInboxRecipient).thenReturn(false) whenever(recipient.isCommunityInboxRecipient).thenReturn(false)
assertThat(viewModel.blindedRecipient, nullValue()) assertThat(viewModel.blindedRecipient, nullValue())
} }
@ -180,7 +179,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
@Test @Test
fun `contact recipient should hide input bar if not accepting requests`() { fun `contact recipient should hide input bar if not accepting requests`() {
whenever(recipient.isOpenGroupInboxRecipient).thenReturn(true) whenever(recipient.isCommunityInboxRecipient).thenReturn(true)
val blinded = mock<Recipient> { val blinded = mock<Recipient> {
whenever(it.blocksCommunityMessageRequests).thenReturn(true) whenever(it.blocksCommunityMessageRequests).thenReturn(true)
} }

View File

@ -74,7 +74,7 @@ class MentionViewModelTest {
threadDatabase = mock { threadDatabase = mock {
on { getRecipientForThreadId(threadID) } doAnswer { on { getRecipientForThreadId(threadID) } doAnswer {
mock<Recipient> { mock<Recipient> {
on { isClosedGroupV2Recipient } doReturn false on { isGroupV2Recipient } doReturn false
on { isCommunityRecipient } doReturn true on { isCommunityRecipient } doReturn true
on { isContactRecipient } doReturn false on { isContactRecipient } doReturn false
} }

View File

@ -41,7 +41,7 @@ sealed class Destination {
address.isContact -> { address.isContact -> {
Contact(address.contactIdentifier()) Contact(address.contactIdentifier())
} }
address.isLegacyClosedGroup -> { address.isLegacyGroup -> {
val groupID = address.toGroupString() val groupID = address.toGroupString()
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString() val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
LegacyClosedGroup(groupPublicKey) LegacyClosedGroup(groupPublicKey)
@ -61,7 +61,7 @@ sealed class Destination {
groupInboxId.last() groupInboxId.last()
) )
} }
address.isClosedGroupV2 -> { address.isGroupV2 -> {
ClosedGroup(address.serialize()) ClosedGroup(address.serialize())
} }
else -> { else -> {

View File

@ -407,7 +407,7 @@ fun MessageReceiver.handleVisibleMessage(
) )
} }
// Handle group invite response if new closed group // Handle group invite response if new closed group
if (threadRecipient?.isClosedGroupV2Recipient == true) { if (threadRecipient?.isGroupV2Recipient == true) {
GlobalScope.launch { GlobalScope.launch {
try { try {
MessagingModuleConfiguration.shared.groupManagerV2 MessagingModuleConfiguration.shared.groupManagerV2
@ -464,7 +464,7 @@ fun MessageReceiver.handleVisibleMessage(
// Cancel any typing indicators if needed // Cancel any typing indicators if needed
cancelTypingIndicatorsIfNeeded(message.sender!!) cancelTypingIndicatorsIfNeeded(message.sender!!)
// Parse reaction if needed // Parse reaction if needed
val threadIsGroup = threadRecipient?.isGroupRecipient == true val threadIsGroup = threadRecipient?.isGroupOrCommunityRecipient == true
message.reaction?.let { reaction -> message.reaction?.let { reaction ->
if (reaction.react == true) { if (reaction.react == true) {
reaction.serverId = message.openGroupServerMessageID?.toString() ?: message.serverHash.orEmpty() reaction.serverId = message.openGroupServerMessageID?.toString() ?: message.serverHash.orEmpty()

View File

@ -18,11 +18,9 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
constructor(`in`: Parcel) : this(`in`.readString()!!) {} constructor(`in`: Parcel) : this(`in`.readString()!!) {}
val isGroup: Boolean val isLegacyGroup: Boolean
get() = GroupUtil.isEncodedGroup(address) || address.startsWith(IdPrefix.GROUP.value)
val isLegacyClosedGroup: Boolean
get() = GroupUtil.isLegacyClosedGroup(address) get() = GroupUtil.isLegacyClosedGroup(address)
val isClosedGroupV2: Boolean val isGroupV2: Boolean
get() = address.startsWith(IdPrefix.GROUP.value) get() = address.startsWith(IdPrefix.GROUP.value)
val isCommunity: Boolean val isCommunity: Boolean
get() = GroupUtil.isCommunity(address) get() = GroupUtil.isCommunity(address)
@ -30,19 +28,23 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
get() = GroupUtil.isCommunityInbox(address) get() = GroupUtil.isCommunityInbox(address)
val isCommunityOutbox: Boolean val isCommunityOutbox: Boolean
get() = address.startsWith(IdPrefix.BLINDED.value) || address.startsWith(IdPrefix.BLINDEDV2.value) get() = address.startsWith(IdPrefix.BLINDED.value) || address.startsWith(IdPrefix.BLINDEDV2.value)
val isGroupOrCommunity: Boolean
get() = isGroup || isCommunity
val isGroup: Boolean
get() = isLegacyGroup || isGroupV2
val isContact: Boolean val isContact: Boolean
get() = !(isGroup || isCommunityInbox) get() = !(isGroupOrCommunity || isCommunityInbox)
fun contactIdentifier(): String { fun contactIdentifier(): String {
if (!isContact && !isCommunity) { if (!isContact && !isCommunity) {
if (isGroup) throw AssertionError("Not e164, is group") if (isGroupOrCommunity) throw AssertionError("Not e164, is group")
throw AssertionError("Not e164, unknown") throw AssertionError("Not e164, unknown")
} }
return address return address
} }
fun toGroupString(): String { fun toGroupString(): String {
if (!isGroup) throw AssertionError("Not group") if (!isGroupOrCommunity) throw AssertionError("Not group")
return address return address
} }

View File

@ -24,9 +24,9 @@ class GroupRecord(
val isOpenGroup: Boolean val isOpenGroup: Boolean
get() = Address.fromSerialized(encodedId).isCommunity get() = Address.fromSerialized(encodedId).isCommunity
val isLegacyClosedGroup: Boolean val isLegacyClosedGroup: Boolean
get() = Address.fromSerialized(encodedId).isLegacyClosedGroup get() = Address.fromSerialized(encodedId).isLegacyGroup
val isClosedGroupV2: Boolean val isClosedGroupV2: Boolean
get() = Address.fromSerialized(encodedId).isClosedGroupV2 get() = Address.fromSerialized(encodedId).isGroupV2
init { init {
if (!TextUtils.isEmpty(members)) { if (!TextUtils.isEmpty(members)) {

View File

@ -8,7 +8,7 @@ fun Recipient.getType(): MessageType =
when{ when{
isCommunityRecipient -> MessageType.COMMUNITY isCommunityRecipient -> MessageType.COMMUNITY
isLocalNumber -> MessageType.NOTE_TO_SELF isLocalNumber -> MessageType.NOTE_TO_SELF
isLegacyClosedGroupRecipient -> MessageType.LEGACY_GROUP isLegacyGroupRecipient -> MessageType.LEGACY_GROUP
isClosedGroupV2Recipient -> MessageType.GROUPS_V2 isGroupV2Recipient -> MessageType.GROUPS_V2
else -> MessageType.ONE_ON_ONE else -> MessageType.ONE_ON_ONE
} }

View File

@ -17,12 +17,9 @@
*/ */
package org.session.libsession.utilities.recipients; package org.session.libsession.utilities.recipients;
import static org.session.libsession.utilities.IdUtilKt.truncateIdForDisplay;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -326,7 +323,7 @@ public class Recipient implements RecipientModifiedListener {
public synchronized @Nullable String getName() { public synchronized @Nullable String getName() {
StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage(); StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage();
String accountID = this.address.toString(); String accountID = this.address.toString();
if (isGroupRecipient()) { if (isGroupOrCommunityRecipient()) {
if (this.name == null) { if (this.name == null) {
List<String> names = new LinkedList<>(); List<String> names = new LinkedList<>();
for (Recipient recipient : participants) { for (Recipient recipient : participants) {
@ -336,7 +333,7 @@ public class Recipient implements RecipientModifiedListener {
} else { } else {
return this.name; return this.name;
} }
} else if (isOpenGroupInboxRecipient()){ } else if (isCommunityInboxRecipient()){
String inboxID = GroupUtil.getDecodedOpenGroupInboxAccountId(accountID); String inboxID = GroupUtil.getDecodedOpenGroupInboxAccountId(accountID);
Contact contact = storage.getContactWithAccountID(inboxID); Contact contact = storage.getContactWithAccountID(inboxID);
if (contact == null) return accountID; if (contact == null) return accountID;
@ -374,7 +371,7 @@ public class Recipient implements RecipientModifiedListener {
} }
public synchronized @NonNull MaterialColor getColor() { public synchronized @NonNull MaterialColor getColor() {
if (isGroupRecipient()) return MaterialColor.GROUP; if (isGroupOrCommunityRecipient()) return MaterialColor.GROUP;
else if (color != null) return color; else if (color != null) return color;
else if (name != null) return ContactColors.generateFor(name); else if (name != null) return ContactColors.generateFor(name);
else return ContactColors.UNKNOWN_COLOR; else return ContactColors.UNKNOWN_COLOR;
@ -458,8 +455,8 @@ public class Recipient implements RecipientModifiedListener {
notifyListeners(); notifyListeners();
} }
public boolean isGroupRecipient() { public boolean isGroupOrCommunityRecipient() {
return address.isGroup(); return address.isGroupOrCommunity();
} }
public boolean isContactRecipient() { public boolean isContactRecipient() {
@ -471,26 +468,26 @@ public class Recipient implements RecipientModifiedListener {
return address.isCommunity(); return address.isCommunity();
} }
public boolean isOpenGroupOutboxRecipient() { public boolean isCommunityOutboxRecipient() {
return address.isCommunityOutbox(); return address.isCommunityOutbox();
} }
public boolean isOpenGroupInboxRecipient() { public boolean isCommunityInboxRecipient() {
return address.isCommunityInbox(); return address.isCommunityInbox();
} }
public boolean isLegacyClosedGroupRecipient() { public boolean isLegacyGroupRecipient() {
return address.isLegacyClosedGroup(); return address.isLegacyGroup();
} }
public boolean isClosedGroupV2Recipient() { public boolean isGroupV2Recipient() {
return address.isClosedGroupV2(); return address.isGroupV2();
} }
@Deprecated @Deprecated
public boolean isPushGroupRecipient() { public boolean isPushGroupRecipient() {
return address.isGroup(); return address.isGroupOrCommunity();
} }
public @NonNull synchronized List<Recipient> getParticipants() { public @NonNull synchronized List<Recipient> getParticipants() {
@ -538,7 +535,7 @@ public class Recipient implements RecipientModifiedListener {
public synchronized @Nullable ContactPhoto getContactPhoto() { public synchronized @Nullable ContactPhoto getContactPhoto() {
if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context)));
else if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId); else if (isGroupOrCommunityRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId);
else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0); else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0);
else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar); else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar);
else return null; else return null;
@ -813,7 +810,7 @@ public class Recipient implements RecipientModifiedListener {
} }
public synchronized boolean showCallMenu() { public synchronized boolean showCallMenu() {
return !isGroupRecipient() && hasApprovedMe(); return !isGroupOrCommunityRecipient() && hasApprovedMe();
} }
@Override @Override

View File

@ -84,9 +84,9 @@ class RecipientProvider {
@NonNull Optional<RecipientSettings> settings, @NonNull Optional<RecipientSettings> settings,
@NonNull Optional<GroupRecord> groupRecord) @NonNull Optional<GroupRecord> groupRecord)
{ {
if (address.isGroup() && settings.isPresent() && groupRecord.isPresent()) { if (address.isGroupOrCommunity() && settings.isPresent() && groupRecord.isPresent()) {
return Optional.of(getGroupRecipientDetails(context, address, groupRecord, settings, true)); return Optional.of(getGroupRecipientDetails(context, address, groupRecord, settings, true));
} else if (!address.isGroup() && settings.isPresent()) { } else if (!address.isGroupOrCommunity() && settings.isPresent()) {
boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context)); boolean isLocalNumber = address.serialize().equals(TextSecurePreferences.getLocalNumber(context));
return Optional.of(new RecipientDetails(null, null, !TextUtils.isEmpty(settings.get().getSystemDisplayName()), isLocalNumber, settings.get(), null)); return Optional.of(new RecipientDetails(null, null, !TextUtils.isEmpty(settings.get().getSystemDisplayName()), isLocalNumber, settings.get(), null));
} }
@ -104,7 +104,7 @@ class RecipientProvider {
} }
private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional<RecipientSettings> settings, Optional<GroupRecord> groupRecord, boolean nestedAsynchronous) { private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional<RecipientSettings> settings, Optional<GroupRecord> groupRecord, boolean nestedAsynchronous) {
if (address.isGroup() && !address.isClosedGroupV2()) return getGroupRecipientDetails(context, address, groupRecord, settings, nestedAsynchronous); if (address.isGroupOrCommunity() && !address.isGroupV2()) return getGroupRecipientDetails(context, address, groupRecord, settings, nestedAsynchronous);
else return getIndividualRecipientDetails(context, address, settings); else return getIndividualRecipientDetails(context, address, settings);
} }