Skip to content

Commit

Permalink
Support conversation muting. (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimOrtel authored Feb 21, 2024
1 parent 52e5ba1 commit da268ee
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ data class ConversationCollections(
val directChats: ConversationCollection<OneToOneChat>,
val hidden: ConversationCollection<Conversation>
) {
val conversations: List<Conversation> get() = favorites.conversations + channels.conversations + groupChats.conversations + directChats.conversations + hidden.conversations

fun filtered(query: String): ConversationCollections {
return ConversationCollections(
channels = channels.filter { it.filterPredicate(query) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Groups2
import androidx.compose.material.icons.filled.NotificationsActive
import androidx.compose.material.icons.filled.NotificationsOff
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -79,6 +82,7 @@ internal fun ConversationList(
onNavigateToConversation: (conversationId: Long) -> Unit,
onToggleMarkAsFavourite: (conversationId: Long, favorite: Boolean) -> Unit,
onToggleHidden: (conversationId: Long, hidden: Boolean) -> Unit,
onToggleMuted: (conversationId: Long, muted: Boolean) -> Unit,
onRequestCreatePersonalConversation: () -> Unit,
onRequestAddChannel: () -> Unit,
trailingContent: LazyListScope.() -> Unit
Expand All @@ -98,7 +102,8 @@ internal fun ConversationList(
conversations = collection,
onNavigateToConversation = onNavigateToConversation,
onToggleMarkAsFavourite = onToggleMarkAsFavourite,
onToggleHidden = onToggleHidden
onToggleHidden = onToggleHidden,
onToggleMuted = onToggleMuted
)
}

Expand Down Expand Up @@ -220,6 +225,7 @@ private fun <T : Conversation> LazyListScope.conversationList(
onNavigateToConversation: (conversationId: Long) -> Unit,
onToggleMarkAsFavourite: (conversationId: Long, favorite: Boolean) -> Unit,
onToggleHidden: (conversationId: Long, hidden: Boolean) -> Unit,
onToggleMuted: (conversationId: Long, muted: Boolean) -> Unit,
) {
if (!conversations.isExpanded) return
items(
Expand All @@ -238,9 +244,13 @@ private fun <T : Conversation> LazyListScope.conversationList(
)
},
onToggleHidden = { onToggleHidden(conversation.id, !conversation.isHidden) },
onToggleMuted = { onToggleMuted(conversation.id, !conversation.isMuted) },
content = { contentModifier ->
val unreadMessagesCount = conversation.unreadMessagesCount ?: 0

val headlineColor =
LocalContentColor.current.copy(alpha = if (conversation.isMuted) 0.6f else 1f)

when (conversation) {
is ChannelChat -> {
val channelName = if (conversation.isArchived) {
Expand All @@ -257,7 +267,7 @@ private fun <T : Conversation> LazyListScope.conversationList(
},
headlineContent = {
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Text(text = channelName, maxLines = 1)
Text(text = channelName, maxLines = 1, color = headlineColor)

ExtraChannelIcons(channelChat = conversation)
}
Expand All @@ -271,7 +281,12 @@ private fun <T : Conversation> LazyListScope.conversationList(
is GroupChat -> {
ListItem(
modifier = contentModifier,
headlineContent = { Text(conversation.humanReadableTitle) },
headlineContent = {
Text(
conversation.humanReadableTitle,
color = headlineColor
)
},
leadingContent = {
Icon(imageVector = Icons.Default.Groups2, contentDescription = null)
},
Expand All @@ -284,7 +299,12 @@ private fun <T : Conversation> LazyListScope.conversationList(
is OneToOneChat -> {
ListItem(
modifier = contentModifier,
headlineContent = { Text(conversation.humanReadableTitle) },
headlineContent = {
Text(
conversation.humanReadableTitle,
color = headlineColor
)
},
trailingContent = {
UnreadMessages(unreadMessagesCount = unreadMessagesCount)
}
Expand Down Expand Up @@ -324,6 +344,7 @@ private fun ConversationListItem(
onNavigateToConversation: () -> Unit,
onToggleMarkAsFavourite: () -> Unit,
onToggleHidden: () -> Unit,
onToggleMuted: () -> Unit,
content: @Composable (Modifier) -> Unit
) {
var isContextDialogShown by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -365,7 +386,7 @@ private fun ConversationListItem(
DropdownMenuItem(
leadingIcon = {
Icon(
imageVector = if (conversation.isHidden) Icons.Default.NotificationsActive else Icons.Default.NotificationsOff,
imageVector = if (conversation.isHidden) Icons.Default.Visibility else Icons.Default.VisibilityOff,
contentDescription = null
)
},
Expand All @@ -382,6 +403,27 @@ private fun ConversationListItem(
onDismissRequest()
}
)

DropdownMenuItem(
leadingIcon = {
Icon(
imageVector = if (conversation.isMuted) Icons.Default.NotificationsActive else Icons.Default.NotificationsOff,
contentDescription = null
)
},
text = {
Text(
text = stringResource(
id = if (conversation.isMuted) R.string.conversation_overview_conversation_item_unmark_as_muted
else R.string.conversation_overview_conversation_item_mark_as_muted
)
)
},
onClick = {
onToggleMuted()
onDismissRequest()
}
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fun ConversationOverviewBody(
},
onToggleMarkAsFavourite = viewModel::markConversationAsFavorite,
onToggleHidden = viewModel::markConversationAsHidden,
onToggleMuted = viewModel::markConversationAsMuted,
onRequestCreatePersonalConversation = onRequestCreatePersonalConversation,
onRequestAddChannel = onRequestAddChannel,
trailingContent = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,24 @@ class ConversationOverviewViewModel(
}
}

fun markConversationAsMuted(conversationId: Long, muted: Boolean): Deferred<Boolean> {
return viewModelScope.async(coroutineContext) {
conversationService.markConversationMuted(
courseId,
conversationId,
muted,
accountService.authToken.first(),
serverConfigurationService.serverUrl.first()
)
.onSuccess { isSuccessful ->
if (isSuccessful) {
onRequestReload.tryEmit(Unit)
}
}
.or(false)
}
}

fun markConversationAsFavorite(conversationId: Long, favorite: Boolean): Deferred<Boolean> {
return viewModelScope.async(coroutineContext) {
conversationService.markConversationAsFavorite(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<string name="conversation_overview_conversation_item_mark_as_hidden">Hide</string>
<string name="conversation_overview_conversation_item_unmark_as_hidden">Unhide</string>

<string name="conversation_overview_conversation_item_mark_as_muted">Mute</string>
<string name="conversation_overview_conversation_item_unmark_as_muted">Unmute</string>

<string name="conversation_overview_section_favorites">Favorites</string>
<string name="conversation_overview_section_channels">Channels</string>
<string name="conversation_overview_section_groups">Group Chats</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,42 @@ class ConversationOverviewE2eTest : ConversationBaseTest() {
)
}

@Test(timeout = DefaultTestTimeoutMillis)
fun `can mark conversation as muted`() {
val chat = runBlocking { createPersonalConversation() }

markConversationImpl(
originalTag = getTagForConversation(chat),
newTag = tagForConversation(chat.id, KEY_SUFFIX_PERSONAL),
textToClick = context.getString(R.string.conversation_overview_conversation_item_mark_as_muted),
checkExists = { conversations.any { conv -> conv.id == chat.id && conv.isMuted } }
)
}

@Test(timeout = DefaultTestTimeoutMillis)
fun `can mark hidden conversation as not muted`() {
val chat = runBlocking {
val chat = createPersonalConversation()

conversationService.markConversationMuted(
courseId = course.id!!,
conversationId = chat.id,
muted = true,
authToken = accessToken,
serverUrl = testServerUrl
).orThrow("Could not mark conversation as hidden")

chat
}

markConversationImpl(
originalTag = tagForConversation(chat.id, KEY_SUFFIX_PERSONAL),
newTag = getTagForConversation(chat),
textToClick = context.getString(R.string.conversation_overview_conversation_item_unmark_as_muted),
checkExists = { conversations.none { conv -> conv.id == chat.id && conv.isMuted } }
)
}

/**
* Checks that updates to conversations are automatically received over the websocket connection.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,12 @@ open class ConversationServiceStub(
authToken: String,
serverUrl: String
): NetworkResponse<Boolean> = NetworkResponse.Failure(StubException)

override suspend fun markConversationMuted(
courseId: Long,
conversationId: Long,
muted: Boolean,
authToken: String,
serverUrl: String
): NetworkResponse<Boolean> = NetworkResponse.Failure(StubException)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class ChannelChat(
override val unreadMessagesCount: Long? = 0L,
override val isFavorite: Boolean = false,
override val isHidden: Boolean = false,
override val isMuted: Boolean = false,
override val isCreator: Boolean = false,
override val isMember: Boolean = false,
override val numberOfMembers: Int = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ sealed class Conversation {
abstract val unreadMessagesCount: Long?
abstract val isFavorite: Boolean
abstract val isHidden: Boolean
abstract val isMuted: Boolean
abstract val isCreator: Boolean
abstract val isMember: Boolean
abstract val numberOfMembers: Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class GroupChat(
override val unreadMessagesCount: Long? = 0L,
override val isFavorite: Boolean = false,
override val isHidden: Boolean = false,
override val isMuted: Boolean = false,
override val isCreator: Boolean = false,
override val isMember: Boolean = false,
override val numberOfMembers: Int = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class OneToOneChat(
override val unreadMessagesCount: Long? = 0L,
override val isFavorite: Boolean = false,
override val isHidden: Boolean = false,
override val isMuted: Boolean = false,
override val isCreator: Boolean = false,
override val isMember: Boolean = false,
override val numberOfMembers: Int = 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ interface ConversationService {
authToken: String,
serverUrl: String
): NetworkResponse<Boolean>

suspend fun markConversationMuted(
courseId: Long,
conversationId: Long,
muted: Boolean,
authToken: String,
serverUrl: String
): NetworkResponse<Boolean>
}

suspend fun ConversationService.getConversation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,24 @@ class ConversationServiceImpl(private val ktorProvider: KtorProvider) : Conversa
appendPathSegments("favorite")
}

override suspend fun markConversationMuted(
courseId: Long,
conversationId: Long,
muted: Boolean,
authToken: String,
serverUrl: String
) = performActionOnConversation(
courseId,
conversationId,
authToken = authToken,
serverUrl = serverUrl,
httpRequestBlock = {
parameter("isMuted", muted)
}
) {
appendPathSegments("muted")
}

private suspend fun performActionOnUser(
courseId: Long,
conversation: Conversation,
Expand Down

0 comments on commit da268ee

Please sign in to comment.