Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@
import io.getstream.chat.android.models.FilterObject
import io.getstream.chat.android.models.Filters
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.Mute
import io.getstream.chat.android.models.TypingEvent
import io.getstream.chat.android.models.User
import io.getstream.chat.android.models.querysort.QuerySortByField
import io.getstream.chat.android.models.querysort.QuerySorter
import io.getstream.chat.android.ui.common.state.channels.actions.ChannelAction
import io.getstream.chat.android.ui.common.utils.extensions.defaultChannelListFilter
import io.getstream.chat.android.ui.common.utils.extensions.isOneToOne
import io.getstream.log.taggedLogger
import io.getstream.result.call.toUnitCall
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -200,7 +202,7 @@
.flatMapLatest { it.channelMutes }
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

private val globalMuted: StateFlow<List<io.getstream.chat.android.models.Mute>> = globalState
private val globalMuted: StateFlow<List<Mute>> = globalState
.flatMapLatest { it.muted }
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

Expand Down Expand Up @@ -432,7 +434,8 @@
channelMutes,
typingChannels,
channelDraftMessages,
) { state, channelMutes, typingChannels, channelDraftMessages ->
globalMuted,
) { state, channelMutes, typingChannels, channelDraftMessages, userMutes ->
when (state) {
ChannelsStateData.NoQueryActive,
ChannelsStateData.Loading,
Expand All @@ -457,6 +460,8 @@
channelItems = createChannelItems(
channels = state.channels,
channelMutes = channelMutes,
userMutes = userMutes,
currentUser = user.value,
typingEvents = typingChannels,
draftMessages = channelDraftMessages.takeIf { isDraftMessageEnabled } ?: emptyMap(),
),
Expand Down Expand Up @@ -800,25 +805,41 @@
*
* @param channels The channels to show.
* @param channelMutes The list of channels muted for the current user.
*
* @param userMutes The list of users muted by the current user.
* @param currentUser The currently logged in user.
*/
@Suppress("LongParameterList")
private fun createChannelItems(
channels: List<Channel>,
channelMutes: List<ChannelMute>,
userMutes: List<Mute>,
currentUser: User?,
typingEvents: Map<String, TypingEvent>,
draftMessages: Map<String, DraftMessage>,
): List<ItemState.ChannelItemState> {
val mutedChannelIds = channelMutes.map { channelMute -> channelMute.channel?.cid }.toSet()
val mutedUserIds = userMutes.mapNotNullTo(mutableSetOf()) { it.target?.id }

Check warning on line 821 in stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/channels/ChannelListViewModel.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make this collection immutable.

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AZ0voBmROWKdm6OU11Cd&open=AZ0voBmROWKdm6OU11Cd&pullRequest=6302
return channels.map {
ItemState.ChannelItemState(
channel = it,
isMuted = it.cid in mutedChannelIds,
isMuted = it.cid in mutedChannelIds || it.isOneToOneMutedByUser(currentUser, mutedUserIds),
typingUsers = typingEvents[it.cid]?.users ?: emptyList(),
draftMessage = draftMessages[it.cid],
)
}
}

/**
* Checks if a 1:1 channel is muted via user mute (i.e. the other member is muted).
*/
private fun Channel.isOneToOneMutedByUser(currentUser: User?, mutedUserIds: Set<String>) =
if (mutedUserIds.isEmpty() || currentUser == null || !isOneToOne(currentUser)) {
false
} else {
val otherUser = members.find { it.user.id != currentUser.id }?.user
otherUser != null && otherUser.id in mutedUserIds
}

internal companion object {
/**
* Default value of number of channels to return when querying channels.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import io.getstream.chat.android.models.ChannelMute
import io.getstream.chat.android.models.FilterObject
import io.getstream.chat.android.models.Filters
import io.getstream.chat.android.models.InitializationState
import io.getstream.chat.android.models.Member
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.Mute
import io.getstream.chat.android.models.OrFilterObject
import io.getstream.chat.android.models.SearchMessagesResult
import io.getstream.chat.android.models.TypingEvent
Expand Down Expand Up @@ -191,6 +193,68 @@ internal class ChannelListViewModelTest {
verify(chatClient).unmuteChannel("messaging", "channel1")
}

@Test
fun `Given a DM with a muted user Should mark the channel as muted`() = runTest {
val viewModel = Fixture()
.givenCurrentUser(currentUser)
.givenChannelsQuery()
.givenChannelsState(
channelsStateData = ChannelsStateData.Result(listOf(directChannel)),
loading = false,
)
.givenChannelMutes()
.givenUserMutes(listOf(otherUserMute))
.givenTypingChannels()
.get(this)

val channelItem = viewModel.channelsState.channelItems.first() as ItemState.ChannelItemState
assertTrue(channelItem.isMuted)
}

@Test
fun `Given a DM without a muted user Should not mark the channel as muted`() = runTest {
val viewModel = Fixture()
.givenCurrentUser(currentUser)
.givenChannelsQuery()
.givenChannelsState(
channelsStateData = ChannelsStateData.Result(listOf(directChannel)),
loading = false,
)
.givenChannelMutes()
.givenTypingChannels()
.get(this)

val channelItem = viewModel.channelsState.channelItems.first() as ItemState.ChannelItemState
assertFalse(channelItem.isMuted)
}

@Test
fun `Given a group channel with a muted user Should not mark the channel as muted`() = runTest {
val groupChannel = Channel(
type = "messaging",
id = "groupChannel",
members = listOf(
Member(user = currentUser),
Member(user = otherUser),
Member(user = User(id = "thirdUser")),
),
)
val viewModel = Fixture()
.givenCurrentUser(currentUser)
.givenChannelsQuery()
.givenChannelsState(
channelsStateData = ChannelsStateData.Result(listOf(groupChannel)),
loading = false,
)
.givenChannelMutes()
.givenUserMutes(listOf(otherUserMute))
.givenTypingChannels()
.get(this)

val channelItem = viewModel.channelsState.channelItems.first() as ItemState.ChannelItemState
assertFalse(channelItem.isMuted)
}

@Test
fun `Given channel list in content state When selecting a channel and dismissing the menu Should hide the menu`() =
runTest {
Expand Down Expand Up @@ -569,6 +633,10 @@ internal class ChannelListViewModelTest {
whenever(globalState.channelMutes) doReturn MutableStateFlow(channelMutes)
}

fun givenUserMutes(userMutes: List<Mute> = emptyList()) = apply {
whenever(globalState.muted) doReturn MutableStateFlow(userMutes)
}

fun givenTypingChannels(typingChannels: Map<String, TypingEvent> = emptyMap()) = apply {
whenever(globalState.typingChannels) doReturn MutableStateFlow(typingChannels)
}
Expand Down Expand Up @@ -649,6 +717,9 @@ internal class ChannelListViewModelTest {
)
private val querySort = QuerySortByField.descByName<Channel>("lastUpdated")

private val currentUser = User(id = "currentUser")
private val otherUser = User(id = "otherUser")

private val channel1: Channel = Channel(
type = "messaging",
id = "channel1",
Expand All @@ -657,5 +728,20 @@ internal class ChannelListViewModelTest {
type = "messaging",
id = "channel2",
)
private val directChannel = Channel(
type = "messaging",
id = "!members-currentUser-otherUser",
members = listOf(
Member(user = currentUser),
Member(user = otherUser),
),
)
private val otherUserMute = Mute(
user = currentUser,
target = otherUser,
createdAt = Date(),
updatedAt = Date(),
expires = null,
)
}
}
Loading