Consolidate duplicated logic to retrieve groups in common.

Merges all of these into GroupsInCommonRepository:
- ConversationSettingsRepository.getGroupsInCommon()
- CallLinkIncomingRequestRepository.getGroupsInCommon()
- ContactSearchPagedDataSourceRepository.getGroupsInCommon()
- ReviewUtil.getGroupsInCommonCount()
- AboutSheetRepository.getGroupsInCommonCount()
This commit is contained in:
Jeffrey Starke
2025-04-08 11:02:29 -04:00
committed by Michelle Tang
parent c9795141df
commit aa7b61ecb1
15 changed files with 112 additions and 144 deletions

View File

@@ -6,11 +6,17 @@
package org.thoughtcrime.securesms.groups
import android.content.Context
import androidx.annotation.Discouraged
import androidx.annotation.WorkerThread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.rx3.asFlow
import kotlinx.coroutines.withContext
import org.signal.core.util.logging.Log
import org.signal.core.util.logging.logV
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -18,22 +24,41 @@ import org.thoughtcrime.securesms.recipients.RecipientId
/**
* Centralizes operations for retrieving groups that a given recipient has in common with another user.
*/
class GroupsInCommonRepository(private val context: Context) {
object GroupsInCommonRepository {
fun getGroupsInCommon(recipientId: RecipientId): Flow<List<Recipient>> {
@WorkerThread
@JvmStatic
@Discouraged("Use getGroupsInCommonCount instead")
fun getGroupsInCommonCountSync(recipientId: RecipientId): Int = runBlocking {
getGroupsInCommonCount(recipientId)
}
suspend fun getGroupsInCommonCount(recipientId: RecipientId): Int = withContext(Dispatchers.IO) {
SignalDatabase.groups
.getPushGroupsContainingMember(recipientId)
.count { it.members.contains(Recipient.self().id) }
}
fun getGroupsInCommonSummary(context: Context, recipientId: RecipientId): Flow<GroupsInCommonSummary> {
return getGroupsInCommon(context, recipientId)
.map(::GroupsInCommonSummary)
}
fun getGroupsInCommon(context: Context, recipientId: RecipientId): Flow<List<Recipient>> {
return Recipient.observable(recipientId)
.asFlow()
.map { recipient ->
if (recipient.hasGroupsInCommon) {
getGroupsContainingRecipient(recipientId)
getGroupsContainingRecipient(context, recipientId)
} else {
emptyList()
}
}
}
private suspend fun getGroupsContainingRecipient(recipientId: RecipientId): List<Recipient> = withContext(Dispatchers.IO) {
SignalDatabase.groups.getPushGroupsContainingMember(recipientId)
private suspend fun getGroupsContainingRecipient(context: Context, recipientId: RecipientId): List<Recipient> = withContext(Dispatchers.IO) {
SignalDatabase.groups
.getPushGroupsContainingMember(recipientId)
.asSequence()
.filter { it.members.contains(Recipient.self().id) }
.map { groupRecord -> Recipient.resolved(groupRecord.recipientId) }
@@ -41,3 +66,35 @@ class GroupsInCommonRepository(private val context: Context) {
.toList()
}
}
/**
* A summary of groups that recipients have in common.
*/
data class GroupsInCommonSummary(
private val groups: List<Recipient>
) {
companion object {
private val TAG = Log.tag(GroupsInCommonSummary::class.java)
}
fun toDisplayText(context: Context, displayGroupsLimit: Int? = null): String {
val displayGroupNames = (displayGroupsLimit?.let(groups::take) ?: groups)
.map { it.getDisplayName(context) }
return when (displayGroupNames.size) {
0 -> "".logV(TAG, "Member with no groups in common!")
1 -> context.getString(R.string.MessageRequestProfileView_member_of_one_group, displayGroupNames[0])
2 -> context.getString(R.string.MessageRequestProfileView_member_of_two_groups, displayGroupNames[0], displayGroupNames[1])
else -> {
val specificGroupNames = displayGroupNames.take(2)
val additionalGroupsCount = displayGroupNames.size - specificGroupNames.size
context.getString(
R.string.MessageRequestProfileView_member_of_many_groups,
specificGroupNames[0],
specificGroupNames[1],
context.resources.getQuantityString(R.plurals.MessageRequestProfileView_member_of_d_additional_groups, additionalGroupsCount, additionalGroupsCount)
)
}
}
}
}

View File

@@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.PassphraseRequiredActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarImage
import org.thoughtcrime.securesms.compose.StatusBarColorNestedScrollConnection
import org.thoughtcrime.securesms.groups.GroupsInCommonRepository
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.CommunicationActions
@@ -74,8 +73,8 @@ class GroupsInCommonActivity : PassphraseRequiredActivity() {
private val viewModel by viewModel {
GroupsInCommonViewModel(
recipientId = intent.getParcelableExtraCompat(EXTRA_RECIPIENT_ID, RecipientId::class.java)!!,
groupsInCommonRepo = GroupsInCommonRepository(context = this)
context = this,
recipientId = intent.getParcelableExtraCompat(EXTRA_RECIPIENT_ID, RecipientId::class.java)!!
)
}
@@ -85,10 +84,10 @@ class GroupsInCommonActivity : PassphraseRequiredActivity() {
setContent {
SignalTheme {
GroupsInCommonScreen(
activity = this,
viewModel = viewModel,
onNavigateBack = ::supportFinishAfterTransition,
onNavigateToConversation = ::navigateToConversation,
activity = this
onNavigateToConversation = ::navigateToConversation
)
}
}
@@ -107,8 +106,8 @@ class GroupsInCommonActivity : PassphraseRequiredActivity() {
@Composable
private fun GroupsInCommonScreen(
viewModel: GroupsInCommonViewModel,
activity: Activity,
viewModel: GroupsInCommonViewModel,
onNavigateBack: () -> Unit = {},
onNavigateToConversation: (recipient: Recipient) -> Unit = {}
) {

View File

@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.groups.ui.incommon
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
@@ -15,11 +16,11 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
class GroupsInCommonViewModel(
recipientId: RecipientId,
groupsInCommonRepo: GroupsInCommonRepository
context: Context,
recipientId: RecipientId
) : ViewModel() {
val groups: StateFlow<List<Recipient>> = groupsInCommonRepo.getGroupsInCommon(recipientId)
val groups: StateFlow<List<Recipient>> = GroupsInCommonRepository.getGroupsInCommon(context, recipientId)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),