From e6c212c8dfe8648a4453dc23d8ac30426192e350 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 23 Nov 2024 17:15:19 +0100 Subject: [PATCH 01/10] Subscribe to both course-wide and normal conversation websockets --- .../feature/metistest/MetisServiceStub.kt | 5 +- .../service/network/MetisService.kt | 3 +- .../service/network/impl/MetisServiceImpl.kt | 18 +++-- .../conversation/ui/ConversationViewModel.kt | 14 ++-- .../ui/ConversationWebSocketUpdateUseCase.kt | 66 ++++++++++++------- 5 files changed, 69 insertions(+), 37 deletions(-) diff --git a/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt b/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt index 677e86c8f..cbba57727 100644 --- a/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt +++ b/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt @@ -32,7 +32,10 @@ class MetisServiceStub( return NetworkResponse.Response(posts.first()) } - override fun subscribeToPostUpdates(metisContext: MetisContext): Flow> { + override fun subscribeToPostUpdates( + courseId: Long, + clientId: Long + ): Flow> { return flowOf() } } \ No newline at end of file diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt index e858583f8..9fd5aeb05 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt @@ -34,7 +34,8 @@ interface MetisService { ): NetworkResponse fun subscribeToPostUpdates( - metisContext: MetisContext + courseId: Long, + clientId: Long, ): Flow> /** diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt index 7d67bb9bf..8130540ca 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt @@ -5,12 +5,12 @@ import de.tum.informatics.www1.artemis.native_app.core.data.cookieAuth import de.tum.informatics.www1.artemis.native_app.core.data.performNetworkCall import de.tum.informatics.www1.artemis.native_app.core.data.service.KtorProvider import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider +import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService +import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.RESOURCE_PATH_SEGMENTS import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisFilter import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisSortingStrategy -import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService -import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.RESOURCE_PATH_SEGMENTS import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.CourseWideContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost import io.ktor.client.call.body @@ -18,6 +18,7 @@ import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.http.appendPathSegments import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.merge internal class MetisServiceImpl( private val ktorProvider: KtorProvider, @@ -148,9 +149,16 @@ internal class MetisServiceImpl( } } - override fun subscribeToPostUpdates(metisContext: MetisContext): Flow> { - val channel = "/topic/metis/courses/${metisContext.courseId}" + override fun subscribeToPostUpdates( + courseId: Long, + clientId: Long + ): Flow> { + val channel = "/topic/metis/courses/$courseId" + val channelConversationNotifications = "/topic/user/$clientId/notifications/conversations" + + val flow1 = websocketProvider.subscribe(channel, MetisPostDTO.serializer()) + val flow2 = websocketProvider.subscribe(channelConversationNotifications, MetisPostDTO.serializer()) - return websocketProvider.subscribe(channel, MetisPostDTO.serializer()) + return merge(flow1, flow2) } } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index f6c320231..b5611d211 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -270,12 +270,14 @@ internal open class ConversationViewModel( // Receive websocket updates and store them in the db. viewModelScope.launch(coroutineContext) { - serverConfigurationService.host.collect { host -> - webSocketUpdateUseCase.updatePosts( - host = host, - context = MetisContext.Conversation(courseId, conversationId) - ) - } + combine(serverConfigurationService.host, clientId.filterSuccess()) { host, clientId -> host to clientId } + .collect { (host, clientId) -> + webSocketUpdateUseCase.updatePosts( + host = host, + context = metisContext, + clientId = clientId + ) + } } } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt index 731139a2b..acfbedef3 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt @@ -1,10 +1,12 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui +import android.util.Log import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService +import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.MetisStorageService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostAction -import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.MetisStorageService +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO /** * Manages updates to the conversation over the web socket. @@ -19,31 +21,47 @@ class ConversationWebSocketUpdateUseCase( */ suspend fun updatePosts( host: String, - context: MetisContext + context: MetisContext, + clientId: Long ) { - metisService.subscribeToPostUpdates(context).collect { websocketData -> + metisService.subscribeToPostUpdates( + courseId = context.courseId, + clientId = clientId + ).collect { websocketData -> if (websocketData is WebsocketProvider.WebsocketData.Message) { - val dto = websocketData.message - when (dto.action) { - MetisPostAction.CREATE -> { - metisStorageService.insertLiveCreatedPost(host, context, dto.post) - } - - MetisPostAction.UPDATE -> { - metisStorageService.updatePost(host, context, dto.post) - } - - MetisPostAction.DELETE -> { - metisStorageService.deletePosts( - host, - listOf(dto.post.id ?: return@collect) - ) - } - - MetisPostAction.NEW_MESSAGE -> { - - } - } + updateDatabaseWithDto( + dto = websocketData.message, + context = context, + host = host + ) + } + } + } + + private suspend fun updateDatabaseWithDto( + dto: MetisPostDTO, + context: MetisContext, + host: String + ) { + Log.d("WebSocket", "Received post update: ${dto.action} ${dto.post}") + when (dto.action) { + MetisPostAction.CREATE -> { + metisStorageService.insertLiveCreatedPost(host, context, dto.post) + } + + MetisPostAction.UPDATE -> { + metisStorageService.updatePost(host, context, dto.post) + } + + MetisPostAction.DELETE -> { + metisStorageService.deletePosts( + host, + listOf(dto.post.id ?: return) + ) + } + + MetisPostAction.NEW_MESSAGE -> { + } } } From d9456435cec9e852db107ec3a38e65d5e3858f70 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 23 Nov 2024 17:35:21 +0100 Subject: [PATCH 02/10] Renamed action to MetisCrudAction, matching server and iOS --- .../metis/conversation/ui/ConversationViewModel.kt | 4 ++-- .../ui/ConversationWebSocketUpdateUseCase.kt | 14 ++++++-------- .../overview/ConversationOverviewViewModel.kt | 10 ++++++---- .../{MetisPostAction.kt => MetisCrudAction.kt} | 4 ++-- .../feature/metis/shared/content/MetisPostDTO.kt | 2 +- .../shared/content/dto/ConversationWebsocketDto.kt | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) rename feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/{MetisPostAction.kt => MetisCrudAction.kt} (58%) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index b5611d211..979911df1 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -42,7 +42,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui. import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.ReplyAutoCompleteHintProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.thread.ConversationThreadUseCase import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostAction +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.AnswerPost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto @@ -233,7 +233,7 @@ internal open class ConversationViewModel( clientId.filterSuccess() ) { conversationDataState, clientId -> websocketProvider.subscribeToConversationUpdates(clientId, metisContext.courseId) - .filter { it.crudAction == MetisPostAction.UPDATE } + .filter { it.crudAction == MetisCrudAction.UPDATE } .map> { DataState.Success(it.conversation) } .onStart { emit(conversationDataState) } } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt index acfbedef3..1afe25d8b 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt @@ -1,11 +1,10 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui -import android.util.Log import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.MetisStorageService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostAction +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO /** @@ -43,25 +42,24 @@ class ConversationWebSocketUpdateUseCase( context: MetisContext, host: String ) { - Log.d("WebSocket", "Received post update: ${dto.action} ${dto.post}") when (dto.action) { - MetisPostAction.CREATE -> { + MetisCrudAction.CREATE -> { metisStorageService.insertLiveCreatedPost(host, context, dto.post) } - MetisPostAction.UPDATE -> { + MetisCrudAction.UPDATE -> { metisStorageService.updatePost(host, context, dto.post) } - MetisPostAction.DELETE -> { + MetisCrudAction.DELETE -> { metisStorageService.deletePosts( host, listOf(dto.post.id ?: return) ) } - MetisPostAction.NEW_MESSAGE -> { - + MetisCrudAction.NEW_MESSAGE -> { + // Nothing to do here. Only relevant for the conversation overview. } } } diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt index 83bf39bb8..cdcf8b958 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt @@ -21,7 +21,7 @@ import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvid import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ConversationCollections.ConversationCollection import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.service.storage.ConversationPreferenceService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostAction +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.ChannelChat import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation @@ -281,13 +281,15 @@ class ConversationOverviewViewModel( is ServerSentConversationUpdate -> { val serverSentUpdate = update.update + // TODO: It seems like there are no updates received from the websocket -> investigate + when (serverSentUpdate.crudAction) { - MetisPostAction.CREATE, MetisPostAction.UPDATE -> { + MetisCrudAction.CREATE, MetisCrudAction.UPDATE -> { currentConversations[serverSentUpdate.conversation.id] = serverSentUpdate.conversation } - MetisPostAction.NEW_MESSAGE -> { + MetisCrudAction.NEW_MESSAGE -> { val isMetisContextVisible = visibleMetisContexts.value.any { visibleMetisContext -> val metisContext = visibleMetisContext.metisContext @@ -305,7 +307,7 @@ class ConversationOverviewViewModel( } } - MetisPostAction.DELETE -> { + MetisCrudAction.DELETE -> { currentConversations.remove(serverSentUpdate.conversation.id) } } diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostAction.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt similarity index 58% rename from feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostAction.kt rename to feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt index fd21d0d9f..6a560cc6c 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostAction.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt @@ -3,9 +3,9 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content import kotlinx.serialization.Serializable @Serializable -enum class MetisPostAction(val value: String) { +enum class MetisCrudAction(val value: String) { CREATE("CREATE"), UPDATE("UPDATE"), DELETE("DELETE"), - NEW_MESSAGE("NEW_MESSAGE") + NEW_MESSAGE("NEW_MESSAGE") // Only used when for the first message in a new conversation. } \ No newline at end of file diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostDTO.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostDTO.kt index d754a69d0..bc9df9013 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostDTO.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisPostDTO.kt @@ -4,4 +4,4 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.d import kotlinx.serialization.Serializable @Serializable -data class MetisPostDTO(val post: StandalonePost, val action: MetisPostAction) \ No newline at end of file +data class MetisPostDTO(val post: StandalonePost, val action: MetisCrudAction) \ No newline at end of file diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt index 03313ca8e..5c45aef16 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt @@ -1,6 +1,6 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostAction +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -9,5 +9,5 @@ import kotlinx.serialization.Serializable data class ConversationWebsocketDto( val conversation: Conversation, @SerialName("metisCrudAction") - val crudAction: MetisPostAction + val crudAction: MetisCrudAction ) From b1bceefb7213653a1235d99668a2c5842783f24e Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 23 Nov 2024 17:49:59 +0100 Subject: [PATCH 03/10] Introduced central WebsocketTopic --- .../core/test/TestWebsocketProvider.kt | 4 +-- .../core/websocket/WebsocketProviderStub.kt | 4 +-- .../core/websocket/WebsocketProvider.kt | 4 +-- .../websocket/impl/WebsocketProviderImpl.kt | 14 +++++----- .../core/websocket/impl/WebsocketTopic.kt | 26 +++++++++++++++++++ .../service/network/impl/MetisServiceImpl.kt | 11 ++++---- .../ConversationWebsocketExtensions.kt | 3 ++- 7 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketTopic.kt diff --git a/core/core-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/test/TestWebsocketProvider.kt b/core/core-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/test/TestWebsocketProvider.kt index f294651cb..5053b60fb 100644 --- a/core/core-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/test/TestWebsocketProvider.kt +++ b/core/core-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/test/TestWebsocketProvider.kt @@ -21,12 +21,12 @@ class TestWebsocketProvider : WebsocketProvider { override val isConnected: Flow = flowOf(true) override fun subscribe( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow> = flowOf(WebsocketProvider.WebsocketData.Subscribe()) override fun subscribeMessage( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow = emptyFlow() diff --git a/core/websocket/src/debug/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProviderStub.kt b/core/websocket/src/debug/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProviderStub.kt index a97fe2f2e..89f33dc61 100644 --- a/core/websocket/src/debug/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProviderStub.kt +++ b/core/websocket/src/debug/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProviderStub.kt @@ -15,12 +15,12 @@ class WebsocketProviderStub : WebsocketProvider { override val isConnected: Flow = flowOf(true) override fun subscribe( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow> = emptyFlow() override fun subscribeMessage( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow = emptyFlow() diff --git a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProvider.kt b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProvider.kt index 08664d037..1b9dbbefe 100644 --- a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProvider.kt +++ b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/WebsocketProvider.kt @@ -19,12 +19,12 @@ interface WebsocketProvider { * Performs automatic reconnects. */ fun subscribe( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow> fun subscribeMessage( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow diff --git a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt index 4b7582409..d3b95821a 100644 --- a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt +++ b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt @@ -199,7 +199,7 @@ class WebsocketProviderImpl( * The given flow can only be subscribed to once. */ override fun subscribe( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow> { return session @@ -207,20 +207,20 @@ class WebsocketProviderImpl( val flow: Flow> = flow { emitAll( currentSession.subscribe( - StompSubscribeHeaders(destination = channel), + StompSubscribeHeaders(destination = topic), deserializer ) ) } .onStart { - Log.d(TAG, "subscribe! $channel") + Log.d(TAG, "subscribe! $topic") emit(WebsocketProvider.WebsocketData.Subscribe()) } .onCompletion { - Log.d(TAG, "unsubscribe! $channel") + Log.d(TAG, "unsubscribe! $topic") } .catch { e -> - Log.d(TAG, "Subscription $channel reported error: ${e.localizedMessage}") + Log.d(TAG, "Subscription $topic reported error: ${e.localizedMessage}") } .map { WebsocketProvider.WebsocketData.Message(it) @@ -247,10 +247,10 @@ class WebsocketProviderImpl( } override fun subscribeMessage( - channel: String, + topic: String, deserializer: DeserializationStrategy ): Flow { - return subscribe(channel, deserializer).mapNotNull { + return subscribe(topic, deserializer).mapNotNull { when (it) { is WebsocketProvider.WebsocketData.Message -> it.message else -> null diff --git a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketTopic.kt b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketTopic.kt new file mode 100644 index 000000000..1f4110635 --- /dev/null +++ b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketTopic.kt @@ -0,0 +1,26 @@ +package de.tum.informatics.www1.artemis.native_app.core.websocket.impl + +object WebsocketTopic { + + /** + * Returns the topic for conversation updates for a course-wide conversation. + */ + fun getCourseWideConversationUpdateTopic(courseId: Long): String { + return "/topic/metis/courses/$courseId" + } + + /** + * Returns the topic for conversation updates for a non-course-wide conversation. + */ + fun getNormalConversationUpdateTopic(userId: Long): String { + return "/topic/user/$userId/notifications/conversations" + } + + /** + * Returns the topic for conversation meta updates. This includes channel creation, deletion, + * and updates (like changing the channel name). + */ + fun getConversationMetaUpdateTopic(courseId: Long, userId: Long): String { + return "/user/topic/metis/courses/$courseId/conversations/user/$userId" + } +} \ No newline at end of file diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt index 8130540ca..14f77a9be 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt @@ -5,6 +5,7 @@ import de.tum.informatics.www1.artemis.native_app.core.data.cookieAuth import de.tum.informatics.www1.artemis.native_app.core.data.performNetworkCall import de.tum.informatics.www1.artemis.native_app.core.data.service.KtorProvider import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider +import de.tum.informatics.www1.artemis.native_app.core.websocket.impl.WebsocketTopic import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.RESOURCE_PATH_SEGMENTS import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext @@ -153,12 +154,12 @@ internal class MetisServiceImpl( courseId: Long, clientId: Long ): Flow> { - val channel = "/topic/metis/courses/$courseId" - val channelConversationNotifications = "/topic/user/$clientId/notifications/conversations" + val courseWideTopic = WebsocketTopic.getCourseWideConversationUpdateTopic(courseId) + val normalTopic = WebsocketTopic.getNormalConversationUpdateTopic(clientId) - val flow1 = websocketProvider.subscribe(channel, MetisPostDTO.serializer()) - val flow2 = websocketProvider.subscribe(channelConversationNotifications, MetisPostDTO.serializer()) + val courseWideUpdates = websocketProvider.subscribe(courseWideTopic, MetisPostDTO.serializer()) + val normalUpdates = websocketProvider.subscribe(normalTopic, MetisPostDTO.serializer()) - return merge(flow1, flow2) + return merge(courseWideUpdates, normalUpdates) } } diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt index 91020cfa6..cb888168c 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt @@ -1,11 +1,12 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service.network import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider +import de.tum.informatics.www1.artemis.native_app.core.websocket.impl.WebsocketTopic import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto import kotlinx.coroutines.flow.Flow fun WebsocketProvider.subscribeToConversationUpdates(userId: Long, courseId: Long): Flow { - val topic = "/user/topic/metis/courses/$courseId/conversations/user/$userId" + val topic = WebsocketTopic.getConversationMetaUpdateTopic(courseId, userId) return subscribeMessage(topic, ConversationWebsocketDto.serializer()) } \ No newline at end of file From 0cc6d89f9625673a5c31044384f5010e12875235 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 24 Nov 2024 20:50:27 +0100 Subject: [PATCH 04/10] Update serialization name for conversationUpdate websocket dto --- .../native_app/core/websocket/impl/WebsocketProviderImpl.kt | 2 +- .../feature/metis/conversation/ui/ConversationViewModel.kt | 2 +- .../ui/conversation/overview/ConversationOverviewViewModel.kt | 2 +- .../metis/shared/content/dto/ConversationWebsocketDto.kt | 4 +--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt index d3b95821a..596c8cf90 100644 --- a/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt +++ b/core/websocket/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/websocket/impl/WebsocketProviderImpl.kt @@ -220,7 +220,7 @@ class WebsocketProviderImpl( Log.d(TAG, "unsubscribe! $topic") } .catch { e -> - Log.d(TAG, "Subscription $topic reported error: ${e.localizedMessage}") + Log.e(TAG, "Subscription $topic reported error: ${e.localizedMessage}") } .map { WebsocketProvider.WebsocketData.Message(it) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index 979911df1..c7f6844d0 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -233,7 +233,7 @@ internal open class ConversationViewModel( clientId.filterSuccess() ) { conversationDataState, clientId -> websocketProvider.subscribeToConversationUpdates(clientId, metisContext.courseId) - .filter { it.crudAction == MetisCrudAction.UPDATE } + .filter { it.action == MetisCrudAction.UPDATE } .map> { DataState.Success(it.conversation) } .onStart { emit(conversationDataState) } } diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt index cdcf8b958..27a67b5ac 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt @@ -283,7 +283,7 @@ class ConversationOverviewViewModel( // TODO: It seems like there are no updates received from the websocket -> investigate - when (serverSentUpdate.crudAction) { + when (serverSentUpdate.action) { MetisCrudAction.CREATE, MetisCrudAction.UPDATE -> { currentConversations[serverSentUpdate.conversation.id] = serverSentUpdate.conversation diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt index 5c45aef16..e0e7fcbeb 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/ConversationWebsocketDto.kt @@ -2,12 +2,10 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content. import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation -import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class ConversationWebsocketDto( val conversation: Conversation, - @SerialName("metisCrudAction") - val crudAction: MetisCrudAction + val action: MetisCrudAction ) From a4fe4f0ff9efe6ab133d132c1154a6e7d0fc994a Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 24 Nov 2024 21:29:59 +0100 Subject: [PATCH 05/10] It seems like the NEW_MESSAGE action is no longer used --- .../ui/ConversationWebSocketUpdateUseCase.kt | 4 -- .../overview/ConversationOverviewViewModel.kt | 40 ++++++++++--------- .../metis/shared/content/MetisCrudAction.kt | 1 - 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt index 1afe25d8b..ba946693a 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt @@ -57,10 +57,6 @@ class ConversationWebSocketUpdateUseCase( listOf(dto.post.id ?: return) ) } - - MetisCrudAction.NEW_MESSAGE -> { - // Nothing to do here. Only relevant for the conversation overview. - } } } } \ No newline at end of file diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt index 27a67b5ac..97597b9de 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt @@ -2,6 +2,7 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversat import android.app.Activity import android.app.Application +import android.util.Log import androidx.lifecycle.viewModelScope import de.tum.informatics.www1.artemis.native_app.core.common.CurrentActivityListener import de.tum.informatics.www1.artemis.native_app.core.common.flatMapLatest @@ -20,7 +21,6 @@ import de.tum.informatics.www1.artemis.native_app.core.device.NetworkStatusProvi import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ConversationCollections.ConversationCollection import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.service.storage.ConversationPreferenceService -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.ChannelChat @@ -281,7 +281,9 @@ class ConversationOverviewViewModel( is ServerSentConversationUpdate -> { val serverSentUpdate = update.update - // TODO: It seems like there are no updates received from the websocket -> investigate + Log.d("ConversationOverviewViewModel", "Received update: ${serverSentUpdate.action} for conversation ${serverSentUpdate.conversation.id}") + + // TODO: Also listen to the websocket updates for the conversation messages when (serverSentUpdate.action) { MetisCrudAction.CREATE, MetisCrudAction.UPDATE -> { @@ -289,23 +291,23 @@ class ConversationOverviewViewModel( serverSentUpdate.conversation } - MetisCrudAction.NEW_MESSAGE -> { - val isMetisContextVisible = - visibleMetisContexts.value.any { visibleMetisContext -> - val metisContext = visibleMetisContext.metisContext - - metisContext is MetisContext.Conversation && metisContext.conversationId == serverSentUpdate.conversation.id - } - - val existingConversation = - currentConversations[serverSentUpdate.conversation.id] - if (existingConversation != null && !isMetisContextVisible) { - currentConversations[serverSentUpdate.conversation.id] = - existingConversation.withUnreadMessagesCount( - (existingConversation.unreadMessagesCount ?: 0) + 1 - ) - } - } +// MetisCrudAction.NEW_MESSAGE -> { +// val isMetisContextVisible = +// visibleMetisContexts.value.any { visibleMetisContext -> +// val metisContext = visibleMetisContext.metisContext +// +// metisContext is MetisContext.Conversation && metisContext.conversationId == serverSentUpdate.conversation.id +// } +// +// val existingConversation = +// currentConversations[serverSentUpdate.conversation.id] +// if (existingConversation != null && !isMetisContextVisible) { +// currentConversations[serverSentUpdate.conversation.id] = +// existingConversation.withUnreadMessagesCount( +// (existingConversation.unreadMessagesCount ?: 0) + 1 +// ) +// } +// } MetisCrudAction.DELETE -> { currentConversations.remove(serverSentUpdate.conversation.id) diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt index 6a560cc6c..9d16d95b0 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/MetisCrudAction.kt @@ -7,5 +7,4 @@ enum class MetisCrudAction(val value: String) { CREATE("CREATE"), UPDATE("UPDATE"), DELETE("DELETE"), - NEW_MESSAGE("NEW_MESSAGE") // Only used when for the first message in a new conversation. } \ No newline at end of file From b7b4ecb55282374434c70158a854e45a14dd5f21 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 24 Nov 2024 22:47:08 +0100 Subject: [PATCH 06/10] Subscribe to postUpdate websocket and increase unread counter for incoming messages --- .../feature/metistest/MetisServiceStub.kt | 11 --- .../service/network/MetisService.kt | 8 -- .../service/network/impl/MetisServiceImpl.kt | 15 ---- .../conversation/ui/ConversationViewModel.kt | 2 +- .../ui/ConversationWebSocketUpdateUseCase.kt | 20 +++-- .../overview/ConversationOverviewViewModel.kt | 81 ++++++++++--------- .../overview/ConversationUpdate.kt | 6 +- .../ConversationWebsocketExtensions.kt | 16 ++++ .../VisibleMetisContext.kt | 5 ++ 9 files changed, 79 insertions(+), 85 deletions(-) diff --git a/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt b/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt index cbba57727..3c307113a 100644 --- a/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt +++ b/feature/metis-test/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metistest/MetisServiceStub.kt @@ -1,13 +1,9 @@ package de.tum.informatics.www1.artemis.native_app.feature.metistest import de.tum.informatics.www1.artemis.native_app.core.data.NetworkResponse -import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf class MetisServiceStub( var posts: List = emptyList() @@ -31,11 +27,4 @@ class MetisServiceStub( ): NetworkResponse { return NetworkResponse.Response(posts.first()) } - - override fun subscribeToPostUpdates( - courseId: Long, - clientId: Long - ): Flow> { - return flowOf() - } } \ No newline at end of file diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt index 9fd5aeb05..1b8cd69d7 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/MetisService.kt @@ -1,14 +1,11 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network import de.tum.informatics.www1.artemis.native_app.core.data.NetworkResponse -import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisFilter -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisSortingStrategy import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.CourseWideContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost -import kotlinx.coroutines.flow.Flow interface MetisService { @@ -33,11 +30,6 @@ interface MetisService { authToken: String ): NetworkResponse - fun subscribeToPostUpdates( - courseId: Long, - clientId: Long, - ): Flow> - /** * The metis context needed to query standalone posts. * @param query if not null the posts will be filtered to contain the given query. diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt index 14f77a9be..2e5698873 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt @@ -5,12 +5,10 @@ import de.tum.informatics.www1.artemis.native_app.core.data.cookieAuth import de.tum.informatics.www1.artemis.native_app.core.data.performNetworkCall import de.tum.informatics.www1.artemis.native_app.core.data.service.KtorProvider import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider -import de.tum.informatics.www1.artemis.native_app.core.websocket.impl.WebsocketTopic import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.RESOURCE_PATH_SEGMENTS import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisFilter -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisSortingStrategy import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.CourseWideContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost @@ -18,8 +16,6 @@ import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.http.appendPathSegments -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.merge internal class MetisServiceImpl( private val ktorProvider: KtorProvider, @@ -150,16 +146,5 @@ internal class MetisServiceImpl( } } - override fun subscribeToPostUpdates( - courseId: Long, - clientId: Long - ): Flow> { - val courseWideTopic = WebsocketTopic.getCourseWideConversationUpdateTopic(courseId) - val normalTopic = WebsocketTopic.getNormalConversationUpdateTopic(clientId) - val courseWideUpdates = websocketProvider.subscribe(courseWideTopic, MetisPostDTO.serializer()) - val normalUpdates = websocketProvider.subscribe(normalTopic, MetisPostDTO.serializer()) - - return merge(courseWideUpdates, normalUpdates) - } } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index c7f6844d0..46701ca9e 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -143,7 +143,7 @@ internal open class ConversationViewModel( * Manages updating from the websocket. */ private val webSocketUpdateUseCase = ConversationWebSocketUpdateUseCase( - metisService = metisService, + websocketProvider = websocketProvider, metisStorageService = metisStorageService ) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt index ba946693a..a0faa8e7c 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationWebSocketUpdateUseCase.kt @@ -1,17 +1,17 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider -import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.MetisStorageService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service.network.subscribeToPostUpdates /** * Manages updates to the conversation over the web socket. */ class ConversationWebSocketUpdateUseCase( - private val metisService: MetisService, + private val websocketProvider: WebsocketProvider, private val metisStorageService: MetisStorageService ) { @@ -23,17 +23,15 @@ class ConversationWebSocketUpdateUseCase( context: MetisContext, clientId: Long ) { - metisService.subscribeToPostUpdates( + websocketProvider.subscribeToPostUpdates( courseId = context.courseId, clientId = clientId - ).collect { websocketData -> - if (websocketData is WebsocketProvider.WebsocketData.Message) { - updateDatabaseWithDto( - dto = websocketData.message, - context = context, - host = host - ) - } + ).collect { postDto -> + updateDatabaseWithDto( + dto = postDto, + context = context, + host = host + ) } } diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt index 97597b9de..a22b688e0 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewViewModel.kt @@ -2,7 +2,6 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversat import android.app.Activity import android.app.Application -import android.util.Log import androidx.lifecycle.viewModelScope import de.tum.informatics.www1.artemis.native_app.core.common.CurrentActivityListener import de.tum.informatics.www1.artemis.native_app.core.common.flatMapLatest @@ -19,9 +18,11 @@ import de.tum.informatics.www1.artemis.native_app.core.datastore.ServerConfigura import de.tum.informatics.www1.artemis.native_app.core.datastore.authToken import de.tum.informatics.www1.artemis.native_app.core.device.NetworkStatusProvider import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider +import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ConversationCollections import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ConversationCollections.ConversationCollection import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.service.storage.ConversationPreferenceService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisCrudAction +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.ChannelChat import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation @@ -29,6 +30,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.d import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.OneToOneChat import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service.network.ConversationService import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service.network.subscribeToConversationUpdates +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service.network.subscribeToPostUpdates import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.ui.MetisViewModel import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.visiblemetiscontextreporter.VisibleMetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.visiblemetiscontextreporter.VisibleMetisContextReporter @@ -134,6 +136,18 @@ class ConversationOverviewViewModel( replay = 0 ) + private val postUpdates: Flow = clientId + .filterSuccess() + .flatMapLatest { userId -> + websocketProvider.subscribeToPostUpdates(courseId, userId) + } + .flowOn(coroutineContext) + .shareIn( + viewModelScope, + SharingStarted.WhileSubscribed(stopTimeout = 5.seconds), + replay = 0 + ) + private val manualConversationUpdates = MutableSharedFlow() /** @@ -180,7 +194,7 @@ class ConversationOverviewViewModel( } .shareIn(viewModelScope + coroutineContext, SharingStarted.Eagerly, replay = 1) - private val conversationsAsCollections: StateFlow> = + private val conversationsAsCollections: StateFlow> = combine( updatedConversations, currentPreferences, @@ -189,7 +203,7 @@ class ConversationOverviewViewModel( conversationsDataState.bind { conversations -> val isFiltering = query.isNotBlank() - de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ConversationCollections( + ConversationCollections( channels = conversations.filterNotHiddenNorFavourite() .filter { !it.filterPredicate("exercise") && !it.filterPredicate("lecture") && !it.filterPredicate("exam") } .asCollection(isFiltering || preferences.generalsExpanded), @@ -231,7 +245,7 @@ class ConversationOverviewViewModel( /** * Holds the latest conversations we could successfully load. */ - private val latestConversations: StateFlow> = + private val latestConversations: StateFlow> = conversationsAsCollections .transformWhile { conversationsAsCollections -> emit(conversationsAsCollections) @@ -245,7 +259,7 @@ class ConversationOverviewViewModel( } .stateIn(viewModelScope + coroutineContext, SharingStarted.Eagerly) - val conversations: StateFlow> = + val conversations: StateFlow> = combine(latestConversations, query) { latestConversationsDataState, query -> if (query.isBlank()) { latestConversationsDataState @@ -265,55 +279,46 @@ class ConversationOverviewViewModel( emit(loadedConversations) merge( + manualConversationUpdates, conversationUpdates.map(::ServerSentConversationUpdate), - manualConversationUpdates + postUpdates.map(::ServerSentPostUpdate) ) .collect { update -> when (update) { is MarkMessagesRead -> { - val affectedConversation = currentConversations[update.conversationId] - if (affectedConversation != null) { - currentConversations[update.conversationId] = - affectedConversation.withUnreadMessagesCount(0L) - } + val affectedConversation = currentConversations[update.conversationId] ?: return@collect + currentConversations[update.conversationId] = affectedConversation.withUnreadMessagesCount(0L) } is ServerSentConversationUpdate -> { - val serverSentUpdate = update.update - - Log.d("ConversationOverviewViewModel", "Received update: ${serverSentUpdate.action} for conversation ${serverSentUpdate.conversation.id}") - - // TODO: Also listen to the websocket updates for the conversation messages + val serverConversationUpdate = update.update - when (serverSentUpdate.action) { + when (serverConversationUpdate.action) { MetisCrudAction.CREATE, MetisCrudAction.UPDATE -> { - currentConversations[serverSentUpdate.conversation.id] = - serverSentUpdate.conversation + currentConversations[serverConversationUpdate.conversation.id] = + serverConversationUpdate.conversation } -// MetisCrudAction.NEW_MESSAGE -> { -// val isMetisContextVisible = -// visibleMetisContexts.value.any { visibleMetisContext -> -// val metisContext = visibleMetisContext.metisContext -// -// metisContext is MetisContext.Conversation && metisContext.conversationId == serverSentUpdate.conversation.id -// } -// -// val existingConversation = -// currentConversations[serverSentUpdate.conversation.id] -// if (existingConversation != null && !isMetisContextVisible) { -// currentConversations[serverSentUpdate.conversation.id] = -// existingConversation.withUnreadMessagesCount( -// (existingConversation.unreadMessagesCount ?: 0) + 1 -// ) -// } -// } - MetisCrudAction.DELETE -> { - currentConversations.remove(serverSentUpdate.conversation.id) + currentConversations.remove(serverConversationUpdate.conversation.id) } } } + + is ServerSentPostUpdate -> { + val postUpdate = update.update + if (postUpdate.action != MetisCrudAction.CREATE) return@collect + + val conversationId = postUpdate.post.conversation?.id ?: return@collect + val isConversationVisible = visibleMetisContexts.value.any { it.isInConversation(conversationId) } + if (isConversationVisible) return@collect // The user is currently looking at the conversation in the UI + + val existingConversation = currentConversations[conversationId] ?: return@collect + + currentConversations[conversationId] = existingConversation.withUnreadMessagesCount( + (existingConversation.unreadMessagesCount ?: 0) + 1 + ) + } } emit(currentConversations.values.toList()) diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationUpdate.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationUpdate.kt index 556430cea..9781131a8 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationUpdate.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationUpdate.kt @@ -1,9 +1,13 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.overview +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto sealed interface ConversationUpdate +data class MarkMessagesRead(val conversationId: Long) : ConversationUpdate + data class ServerSentConversationUpdate(val update: ConversationWebsocketDto) : ConversationUpdate -data class MarkMessagesRead(val conversationId: Long) : ConversationUpdate \ No newline at end of file +data class ServerSentPostUpdate(val update: MetisPostDTO) : ConversationUpdate + diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt index cb888168c..485b39894 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/service/network/ConversationWebsocketExtensions.kt @@ -2,11 +2,27 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.service. import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.core.websocket.impl.WebsocketTopic +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisPostDTO import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.ConversationWebsocketDto import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.merge fun WebsocketProvider.subscribeToConversationUpdates(userId: Long, courseId: Long): Flow { val topic = WebsocketTopic.getConversationMetaUpdateTopic(courseId, userId) return subscribeMessage(topic, ConversationWebsocketDto.serializer()) +} + + +fun WebsocketProvider.subscribeToPostUpdates( + courseId: Long, + clientId: Long +): Flow { + val courseWideTopic = WebsocketTopic.getCourseWideConversationUpdateTopic(courseId) + val normalTopic = WebsocketTopic.getNormalConversationUpdateTopic(clientId) + + val courseWideUpdates = subscribeMessage(courseWideTopic, MetisPostDTO.serializer()) + val normalUpdates = subscribeMessage(normalTopic, MetisPostDTO.serializer()) + + return merge(courseWideUpdates, normalUpdates) } \ No newline at end of file diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/visiblemetiscontextreporter/VisibleMetisContext.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/visiblemetiscontextreporter/VisibleMetisContext.kt index 40b94e5ee..4aea88e2e 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/visiblemetiscontextreporter/VisibleMetisContext.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/visiblemetiscontextreporter/VisibleMetisContext.kt @@ -5,6 +5,11 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.M sealed interface VisibleMetisContext { val metisContext: MetisContext + + fun isInConversation(conversationId: Long): Boolean { + return metisContext is MetisContext.Conversation && + (metisContext as MetisContext.Conversation).conversationId == conversationId + } } data class VisiblePostList(override val metisContext: MetisContext) : VisibleMetisContext From 4cade67aad7eefaeb1180da16de9f85907caff79 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 24 Nov 2024 22:49:44 +0100 Subject: [PATCH 07/10] Remove unused parameter --- .../feature/metis/conversation/conversation_module.kt | 2 +- .../conversation/service/network/impl/MetisServiceImpl.kt | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/conversation_module.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/conversation_module.kt index 38fb47cc8..2479b3b50 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/conversation_module.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/conversation_module.kt @@ -21,7 +21,7 @@ import org.koin.androidx.workmanager.dsl.workerOf import org.koin.dsl.module val conversationModule = module { - single { MetisServiceImpl(get(), get()) } + single { MetisServiceImpl(get()) } single { MetisModificationServiceImpl(get()) } single { EmojiServiceImpl(androidContext()) } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt index 2e5698873..942753ea1 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/network/impl/MetisServiceImpl.kt @@ -4,7 +4,6 @@ import de.tum.informatics.www1.artemis.native_app.core.data.NetworkResponse import de.tum.informatics.www1.artemis.native_app.core.data.cookieAuth import de.tum.informatics.www1.artemis.native_app.core.data.performNetworkCall import de.tum.informatics.www1.artemis.native_app.core.data.service.KtorProvider -import de.tum.informatics.www1.artemis.native_app.core.websocket.WebsocketProvider import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.RESOURCE_PATH_SEGMENTS import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext @@ -19,7 +18,6 @@ import io.ktor.http.appendPathSegments internal class MetisServiceImpl( private val ktorProvider: KtorProvider, - private val websocketProvider: WebsocketProvider ) : MetisService { override suspend fun getPosts( @@ -145,6 +143,4 @@ internal class MetisServiceImpl( } } } - - } From b737b318f9dd668090aa957709fb95b403be6a21 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 1 Dec 2024 21:15:56 +0100 Subject: [PATCH 08/10] Fix unread counter padding --- .../ui/conversation/overview/ConversationList.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt index 4863750f6..1f0b14d62 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt @@ -366,10 +366,7 @@ private fun ConversationListItem( } }, trailingContent = { - UnreadMessages( - modifier = Modifier.padding(end = 24.dp), - unreadMessagesCount = unreadMessagesCount - ) + UnreadMessages(unreadMessagesCount = unreadMessagesCount) } ) } @@ -515,6 +512,7 @@ private fun UnreadMessages(modifier: Modifier = Modifier, unreadMessagesCount: L if (unreadMessagesCount > 0) { Box( modifier = modifier + .padding(end = 24.dp) .size(24.dp) .aspectRatio(1f) .background( From 15f4c0903ca07639ed7d89d137649e4faa8607af Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 1 Dec 2024 21:19:55 +0100 Subject: [PATCH 09/10] Replace deprecated icons --- .../conversation/overview/ConversationList.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt index 1f0b14d62..a56d229f0 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt @@ -16,16 +16,16 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowRight +import androidx.compose.material.icons.automirrored.filled.InsertDriveFile +import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.automirrored.filled.Message import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.ArrowRight import androidx.compose.material.icons.filled.ChatBubble import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.icons.filled.Forum import androidx.compose.material.icons.filled.Groups2 -import androidx.compose.material.icons.filled.InsertDriveFile -import androidx.compose.material.icons.filled.List -import androidx.compose.material.icons.filled.Message import androidx.compose.material.icons.filled.MoreHoriz import androidx.compose.material.icons.filled.NotInterested import androidx.compose.material.icons.filled.NotificationsActive @@ -33,9 +33,9 @@ import androidx.compose.material.icons.filled.NotificationsOff import androidx.compose.material.icons.filled.School 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.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -153,7 +153,7 @@ internal fun ConversationList( R.string.conversation_overview_section_exercise_channels, NoAction, viewModel::toggleExercisesExpanded - ) { Icon(imageVector = Icons.Default.List, contentDescription = null) } + ) { Icon(imageVector = Icons.AutoMirrored.Filled.List, contentDescription = null) } } if (conversationCollections.lectureChannels.conversations.isNotEmpty()) { @@ -164,7 +164,7 @@ internal fun ConversationList( R.string.conversation_overview_section_lecture_channels, NoAction, viewModel::toggleLecturesExpanded - ) { Icon(imageVector = Icons.Default.InsertDriveFile, contentDescription = null) } + ) { Icon(imageVector = Icons.AutoMirrored.Filled.InsertDriveFile, contentDescription = null) } } if (conversationCollections.examChannels.conversations.isNotEmpty()) { @@ -197,7 +197,7 @@ internal fun ConversationList( R.string.conversation_overview_section_direct_messages, OnClickAction(onRequestCreatePersonalConversation), viewModel::togglePersonalConversationsExpanded - ) { Icon(imageVector = Icons.Default.Message, contentDescription = null) } + ) { Icon(imageVector = Icons.AutoMirrored.Filled.Message, contentDescription = null) } } if (conversationCollections.hidden.conversations.isNotEmpty()) { @@ -229,7 +229,7 @@ private fun LazyListScope.conversationSectionHeader( .fillMaxWidth() .testTag(key) ) { - Divider() + HorizontalDivider() Row( modifier = Modifier @@ -258,14 +258,14 @@ private fun LazyListScope.conversationSectionHeader( onClick = { toggleIsExpanded() } ) { Icon( - imageVector = if (isExpanded) Icons.Default.ArrowDropDown else Icons.Default.ArrowRight, + imageVector = if (isExpanded) Icons.Default.ArrowDropDown else Icons.AutoMirrored.Filled.ArrowRight, contentDescription = null, modifier = Modifier.size(32.dp) ) } } - Divider() + HorizontalDivider() } } } From ad915491980e9ab1497148dbfa6849b5974b3818 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sun, 1 Dec 2024 21:24:59 +0100 Subject: [PATCH 10/10] Removed unused ConversationSectionHeaderAction --- .../conversation/overview/ConversationList.kt | 32 ++++--------------- .../overview/ConversationOverviewBody.kt | 6 ++-- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt index a56d229f0..ad79ccadc 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationList.kt @@ -96,19 +96,16 @@ internal fun ConversationList( 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 ) { - val listWithHeader: LazyListScope.(ConversationCollections.ConversationCollection<*>, String, String, Int, ConversationSectionHeaderAction, () -> Unit, @Composable () -> Unit) -> Unit = - { collection, key, suffix, textRes, action, toggleIsExpanded, icon -> + val listWithHeader: LazyListScope.(ConversationCollections.ConversationCollection<*>, String, String, Int, () -> Unit, @Composable () -> Unit) -> Unit = + { collection, key, suffix, textRes, onClick, icon -> conversationSectionHeader( key = key, text = textRes, - onClickAddAction = action, isExpanded = collection.isExpanded, - toggleIsExpanded = toggleIsExpanded, + onClick = onClick, icon = icon ) @@ -130,8 +127,7 @@ internal fun ConversationList( SECTION_FAVORITES_KEY, KEY_SUFFIX_FAVORITES, R.string.conversation_overview_section_favorites, - NoAction, - { viewModel.toggleFavoritesExpanded() }, + viewModel::toggleFavoritesExpanded, { Icon(imageVector = Icons.Default.Favorite, contentDescription = null) } ) } @@ -141,7 +137,6 @@ internal fun ConversationList( SECTION_CHANNELS_KEY, KEY_SUFFIX_CHANNELS, R.string.conversation_overview_section_general_channels, - OnClickAction(onRequestAddChannel), viewModel::toggleGeneralsExpanded ) { Icon(imageVector = Icons.Default.ChatBubble, contentDescription = null) } @@ -151,7 +146,6 @@ internal fun ConversationList( SECTION_EXERCISES_KEY, KEY_SUFFIX_EXERCISES, R.string.conversation_overview_section_exercise_channels, - NoAction, viewModel::toggleExercisesExpanded ) { Icon(imageVector = Icons.AutoMirrored.Filled.List, contentDescription = null) } } @@ -162,7 +156,6 @@ internal fun ConversationList( SECTION_LECTURES_KEY, KEY_SUFFIX_LECTURES, R.string.conversation_overview_section_lecture_channels, - NoAction, viewModel::toggleLecturesExpanded ) { Icon(imageVector = Icons.AutoMirrored.Filled.InsertDriveFile, contentDescription = null) } } @@ -173,7 +166,6 @@ internal fun ConversationList( SECTION_EXAMS_KEY, KEY_SUFFIX_EXAMS, R.string.conversation_overview_section_exam_channels, - NoAction, viewModel::toggleExamsExpanded ) { Icon(imageVector = Icons.Default.School, contentDescription = null) } } @@ -184,7 +176,6 @@ internal fun ConversationList( SECTION_GROUPS_KEY, KEY_SUFFIX_GROUPS, R.string.conversation_overview_section_groups, - OnClickAction(onRequestCreatePersonalConversation), viewModel::toggleGroupChatsExpanded ) { Icon(imageVector = Icons.Default.Forum, contentDescription = null) } } @@ -195,7 +186,6 @@ internal fun ConversationList( SECTION_DIRECT_MESSAGES_KEY, KEY_SUFFIX_PERSONAL, R.string.conversation_overview_section_direct_messages, - OnClickAction(onRequestCreatePersonalConversation), viewModel::togglePersonalConversationsExpanded ) { Icon(imageVector = Icons.AutoMirrored.Filled.Message, contentDescription = null) } } @@ -206,7 +196,6 @@ internal fun ConversationList( SECTION_HIDDEN_KEY, KEY_SUFFIX_HIDDEN, R.string.conversation_overview_section_hidden, - NoAction, viewModel::toggleHiddenExpanded ) { Icon(imageVector = Icons.Default.NotInterested, contentDescription = null) } } @@ -219,8 +208,7 @@ private fun LazyListScope.conversationSectionHeader( key: String, @StringRes text: Int, isExpanded: Boolean, - onClickAddAction: ConversationSectionHeaderAction, - toggleIsExpanded: () -> Unit, + onClick: () -> Unit, icon: @Composable () -> Unit ) { item(key = key) { @@ -234,7 +222,7 @@ private fun LazyListScope.conversationSectionHeader( Row( modifier = Modifier .fillMaxWidth() - .clickable { toggleIsExpanded() } + .clickable { onClick() } .padding(horizontal = 16.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween @@ -255,7 +243,7 @@ private fun LazyListScope.conversationSectionHeader( IconButton( modifier = Modifier.testTag(TEST_TAG_HEADER_EXPAND_ICON), - onClick = { toggleIsExpanded() } + onClick = { onClick() } ) { Icon( imageVector = if (isExpanded) Icons.Default.ArrowDropDown else Icons.AutoMirrored.Filled.ArrowRight, @@ -529,12 +517,6 @@ private fun UnreadMessages(modifier: Modifier = Modifier, unreadMessagesCount: L } } -private sealed interface ConversationSectionHeaderAction - -private data class OnClickAction(val onClick: () -> Unit) : ConversationSectionHeaderAction - -private object NoAction : ConversationSectionHeaderAction - private fun String.removeSectionPrefix(): String { val prefixes = listOf("exercise-", "lecture-", "exam-") var result = this diff --git a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewBody.kt b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewBody.kt index d27066657..dd5dc33f1 100644 --- a/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewBody.kt +++ b/feature/metis/manage-conversations/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/manageconversations/ui/conversation/overview/ConversationOverviewBody.kt @@ -26,10 +26,10 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Tag import androidx.compose.material.icons.filled.WifiOff -import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -151,10 +151,8 @@ fun ConversationOverviewBody( onToggleMarkAsFavourite = viewModel::markConversationAsFavorite, onToggleHidden = viewModel::markConversationAsHidden, onToggleMuted = viewModel::markConversationAsMuted, - onRequestCreatePersonalConversation = onRequestCreatePersonalConversation, - onRequestAddChannel = onRequestAddChannel, trailingContent = { - item { Divider() } + item { HorizontalDivider() } item(key = KEY_BUTTON_SHOW_COC) { Box(