Skip to content

Commit

Permalink
resolved merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
julian-wls committed Nov 15, 2024
2 parents 92f76c7 + 1d9afac commit 0fcec6e
Show file tree
Hide file tree
Showing 29 changed files with 893 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ val testWebsocketModule = module {
single<WebsocketProvider> { TestWebsocketProvider() }
}

private class TestWebsocketProvider : WebsocketProvider {
class TestWebsocketProvider : WebsocketProvider {

override val connectionState: Flow<WebsocketProvider.WebsocketConnectionState> =
flowOf(WebsocketProvider.WebsocketConnectionState.WithSession(true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object ArtemisInstances {
)

private val TumTs1 = ArtemisInstance(
host = "artemis-test1.artemis.in.tum.de",
host = "artemis-test1.artemis.cit.tum.de",
name = R.string.artemis_instance_tum_test_server_1,
type = ArtemisInstance.Type.TEST
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ fun `Metis - Conversation Channel`() {
),
clientId = 0L,
hasModerationRights = true,
isAtLeastTutorInCourse = true,
listContentPadding = PaddingValues(),
serverUrl = "",
courseId = 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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<StandalonePost> = emptyList()
): MetisService {

override suspend fun getPosts(
standalonePostsContext: MetisService.StandalonePostsContext,
pageSize: Int,
pageNum: Int,
authToken: String,
serverUrl: String
): NetworkResponse<List<StandalonePost>> {
return NetworkResponse.Response(posts)
}

override suspend fun getPost(
metisContext: MetisContext,
serverSidePostId: Long,
serverUrl: String,
authToken: String
): NetworkResponse<StandalonePost> {
return NetworkResponse.Response(posts.first())
}

override fun subscribeToPostUpdates(metisContext: MetisContext): Flow<WebsocketProvider.WebsocketData<MetisPostDTO>> {
return flowOf()
}
}
3 changes: 3 additions & 0 deletions feature/metis/conversation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ dependencies {

testImplementation(project(":feature:metis-test"))
implementation("androidx.paging:paging-common:3.2.1")

testImplementation(libs.mockk.android)
testImplementation(libs.mockk.agent)
}

tasks.register("fetchAndPrepareEmojis", emoji.FetchAndPrepareEmojisTask::class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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.model.Course
import de.tum.informatics.www1.artemis.native_app.core.model.account.isAtLeastTutorInCourse
import de.tum.informatics.www1.artemis.native_app.core.model.exercise.FileUploadExercise
import de.tum.informatics.www1.artemis.native_app.core.model.exercise.ModelingExercise
import de.tum.informatics.www1.artemis.native_app.core.model.exercise.ProgrammingExercise
Expand Down Expand Up @@ -149,6 +150,21 @@ internal open class ConversationViewModel(
metisStorageService = metisStorageService
)

private val course: StateFlow<DataState<Course>> = flatMapLatest(
serverConfigurationService.serverUrl,
accountService.authToken,
onRequestReload.onStart { emit(Unit) }
) { serverUrl, authToken, _ ->
retryOnInternet(networkStatusProvider.currentNetworkStatus) {
courseService.getCourse(
metisContext.courseId,
serverUrl,
authToken
).bind { it.course }
}
}
.stateIn(viewModelScope + coroutineContext, SharingStarted.Lazily)

val hasModerationRights: StateFlow<Boolean> = flatMapLatest(
serverConfigurationService.serverUrl,
accountService.authToken,
Expand All @@ -168,6 +184,22 @@ internal open class ConversationViewModel(
}
.stateIn(viewModelScope + coroutineContext, SharingStarted.Eagerly, false)

val isAtLeastTutorInCourse: StateFlow<Boolean> = flatMapLatest(
serverConfigurationService.serverUrl,
accountService.authToken,
course,
onRequestReload.onStart { emit(Unit) }
) { serverUrl, authToken, course, _ ->
retryOnInternet(networkStatusProvider.currentNetworkStatus) {
accountDataService.getAccountData(
serverUrl = serverUrl,
bearerToken = authToken
)
.bind { it.isAtLeastTutorInCourse(course = course.orThrow()) }
}
.map { it.orElse(false) }
}
.stateIn(viewModelScope + coroutineContext, SharingStarted.Eagerly, false)

val conversationDataStatus: StateFlow<DataStatus> = combine(
websocketProvider.isConnected,
Expand Down Expand Up @@ -210,21 +242,6 @@ internal open class ConversationViewModel(
}
.stateIn(viewModelScope + coroutineContext, SharingStarted.Eagerly)

private val course: StateFlow<DataState<Course>> = flatMapLatest(
serverConfigurationService.serverUrl,
accountService.authToken,
onRequestReload.onStart { emit(Unit) }
) { serverUrl, authToken, _ ->
retryOnInternet(networkStatusProvider.currentNetworkStatus) {
courseService.getCourse(
metisContext.courseId,
serverUrl,
authToken
).bind { it.course }
}
}
.stateIn(viewModelScope + coroutineContext, SharingStarted.Lazily)

private val conversations: StateFlow<DataState<List<Conversation>>> = flatMapLatest(
serverConfigurationService.serverUrl,
accountService.authToken,
Expand Down Expand Up @@ -325,6 +342,32 @@ internal open class ConversationViewModel(
return if (success) null else MetisModificationFailure.DELETE_REACTION
}

/**
* Handles a click on resolve or does not resolve post.
* It updates the post accordingly.
*/
fun toggleResolvePost(
parentPost: PostPojo,
post: AnswerPostPojo
): Deferred<MetisModificationFailure?> {
return viewModelScope.async(coroutineContext) {
val conversation =
loadConversation() ?: return@async MetisModificationFailure.UPDATE_POST

val resolved = !post.resolvesPost
val serializedParentPost = StandalonePost(parentPost, conversation)
val newPost = AnswerPost(post, serializedParentPost).copy(resolvesPost = resolved)

metisModificationService.updateAnswerPost(
context = metisContext,
post = newPost,
serverUrl = serverConfigurationService.serverUrl.first(),
authToken = accountService.authToken.first()
)
.asMetisModificationFailure(MetisModificationFailure.UPDATE_POST)
}
}

fun deletePost(post: IBasePost): Deferred<MetisModificationFailure?> {
return viewModelScope.async(coroutineContext) {
metisModificationService.deletePost(
Expand Down Expand Up @@ -420,12 +463,9 @@ internal open class ConversationViewModel(
) { authToken, serverUrl ->
retryOnInternet(networkStatusProvider.currentNetworkStatus) {
conversationService
.searchForPotentialCommunicationParticipants(
.searchForCourseMembers(
courseId = metisContext.courseId,
query = query,
includeStudents = true,
includeTutors = true,
includeInstructors = true,
authToken = authToken,
serverUrl = serverUrl
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.chatlist

import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IStandalonePost
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.pojo.PostPojo
import kotlinx.datetime.LocalDate

sealed class ChatListItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.toJavaInstant
import org.koin.compose.koinInject
import java.text.SimpleDateFormat
import java.util.Date

Expand All @@ -75,6 +76,7 @@ internal fun MetisChatList(

val clientId: Long by viewModel.clientIdOrDefault.collectAsState()
val hasModerationRights by viewModel.hasModerationRights.collectAsState()
val isAtLeastTutorInCourse by viewModel.isAtLeastTutorInCourse.collectAsState()

val serverUrl by viewModel.serverUrl.collectAsState()

Expand All @@ -94,6 +96,7 @@ internal fun MetisChatList(
posts = posts.asPostsDataState(),
clientId = clientId,
hasModerationRights = hasModerationRights,
isAtLeastTutorInCourse = isAtLeastTutorInCourse,
listContentPadding = listContentPadding,
serverUrl = serverUrl,
courseId = viewModel.courseId,
Expand All @@ -119,6 +122,7 @@ fun MetisChatList(
bottomItem: PostPojo?,
clientId: Long,
hasModerationRights: Boolean,
isAtLeastTutorInCourse: Boolean,
listContentPadding: PaddingValues,
serverUrl: String,
courseId: Long,
Expand All @@ -137,9 +141,10 @@ fun MetisChatList(
initialReplyTextProvider = initialReplyTextProvider,
onCreatePost = onCreatePost,
onEditPost = onEditPost,
onResolvePost = null,
onDeletePost = onDeletePost,
onRequestReactWithEmoji = onRequestReactWithEmoji
) { replyMode, onEditPostDelegate, onRequestReactWithEmojiDelegate, onDeletePostDelegate, updateFailureStateDelegate ->
onRequestReactWithEmoji = onRequestReactWithEmoji,
) { replyMode, onEditPostDelegate, _, onRequestReactWithEmojiDelegate, onDeletePostDelegate, updateFailureStateDelegate ->
Column(modifier = modifier) {
val informationModifier = Modifier
.fillMaxSize()
Expand All @@ -155,7 +160,8 @@ fun MetisChatList(
itemCount = posts.itemCount,
order = DisplayPostOrder.REVERSED,
bottomItem = bottomItem,
imageLoaderCreation = imageLoaderCreation
imageLoaderCreation = imageLoaderCreation,
emojiService = koinInject()
) {
when (posts) {
PostsDataState.Empty -> {
Expand Down Expand Up @@ -186,6 +192,7 @@ fun MetisChatList(
clientId = clientId,
onClickViewPost = onClickViewPost,
hasModerationRights = hasModerationRights,
isAtLeastTutorInCourse = isAtLeastTutorInCourse,
onRequestEdit = onEditPostDelegate,
onRequestDelete = onDeletePostDelegate,
onRequestReactWithEmoji = onRequestReactWithEmojiDelegate,
Expand Down Expand Up @@ -214,6 +221,7 @@ private fun ChatList(
state: LazyListState,
posts: PostsDataState.Loaded,
hasModerationRights: Boolean,
isAtLeastTutorInCourse: Boolean,
clientId: Long,
onClickViewPost: (StandalonePostId) -> Unit,
onRequestEdit: (IStandalonePost) -> Unit,
Expand Down Expand Up @@ -246,6 +254,7 @@ private fun ChatList(
val postActions = rememberPostActions(
post = post,
hasModerationRights = hasModerationRights,
isAtLeastTutorInCourse = isAtLeastTutorInCourse,
clientId = clientId,
onRequestEdit = { onRequestEdit(post ?: return@rememberPostActions) },
onRequestDelete = {
Expand All @@ -257,6 +266,7 @@ private fun ChatList(
onReplyInThread = {
onClickViewPost(post?.standalonePostId ?: return@rememberPostActions)
},
onResolvePost = null,
onRequestRetrySend = {
onRequestRetrySend(
post?.standalonePostId ?: return@rememberPostActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import coil.ImageLoader
import de.tum.informatics.www1.artemis.native_app.core.common.markdown.PostArtemisMarkdownTransformer
import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.LocalMarkdownTransformer
import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.ProvideMarkwon
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.EmojiService
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.impl.EmojiServiceStub
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.ProvideEmojis
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.DisplayPostOrder
import kotlinx.coroutines.Deferred
Expand All @@ -45,6 +47,7 @@ internal fun <T : Any> MetisPostListHandler(
bottomItem: T?,
order: DisplayPostOrder,
imageLoaderCreation: (Context) -> Deferred<ImageLoader>,
emojiService: EmojiService,
content: @Composable BoxScope.() -> Unit
) {
val scope = rememberCoroutineScope()
Expand Down Expand Up @@ -127,7 +130,7 @@ internal fun <T : Any> MetisPostListHandler(
}

ProvideMarkwon(imageLoader = imageLoader) {
ProvideEmojis {
ProvideEmojis(emojiService) {
CompositionLocalProvider(LocalMarkdownTransformer provides markdownTransformer) {
content()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.paging.PagingState
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.dto.StandalonePost


class MetisSearchPagingSource(
private val metisService: MetisService,
private val context: MetisService.StandalonePostsContext,
Expand All @@ -30,8 +31,12 @@ class MetisSearchPagingSource(
)
.map(
mapSuccess = { loadedPosts ->
// Currently the server returns duplicate posts when searching for posts. This
// caused a crash in the UI.
// TODO: https://github.com/ls1intum/artemis-android/issues/99
val filteredPosts = loadedPosts.distinctBy { it.id }
LoadResult.Page(
data = loadedPosts,
data = filteredPosts,
prevKey = if (page > 0) page - 1 else null,
nextKey = if (loadedPosts.size == params.loadSize) page + 1 else null
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IAnswerPost
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IBasePost

data class PostActions(
Expand All @@ -12,6 +13,7 @@ data class PostActions(
val onClickReaction: ((emojiId: String, create: Boolean) -> Unit)? = null,
val onCopyText: () -> Unit = {},
val onReplyInThread: (() -> Unit)? = null,
val onResolvePost: (() -> Unit)? = null,
val onRequestRetrySend: () -> Unit = {}
) {
val canPerformAnyAction: Boolean get() = requestDeletePost != null || requestEditPost != null
Expand All @@ -21,11 +23,13 @@ data class PostActions(
fun rememberPostActions(
post: IBasePost?,
hasModerationRights: Boolean,
isAtLeastTutorInCourse: Boolean,
clientId: Long,
onRequestEdit: () -> Unit,
onRequestDelete: () -> Unit,
onClickReaction: (emojiId: String, create: Boolean) -> Unit,
onReplyInThread: (() -> Unit)?,
onResolvePost: (() -> Unit)?,
onRequestRetrySend: () -> Unit
): PostActions {
val clipboardManager = LocalClipboardManager.current
Expand All @@ -38,12 +42,14 @@ fun rememberPostActions(
onRequestDelete,
onClickReaction,
onReplyInThread,
onResolvePost,
onRequestRetrySend,
clipboardManager
) {
if (post != null) {
val doesPostExistOnServer = post.serverPostId != null
val hasEditPostRights = hasModerationRights || post.authorId == clientId
val hasResolvePostRights = isAtLeastTutorInCourse || post.authorId == clientId

PostActions(
requestEditPost = if (doesPostExistOnServer && hasEditPostRights) onRequestEdit else null,
Expand All @@ -53,6 +59,7 @@ fun rememberPostActions(
clipboardManager.setText(AnnotatedString(post.content.orEmpty()))
},
onReplyInThread = if (doesPostExistOnServer) onReplyInThread else null,
onResolvePost = if (hasResolvePostRights) onResolvePost else null,
onRequestRetrySend = onRequestRetrySend
)
} else {
Expand Down
Loading

0 comments on commit 0fcec6e

Please sign in to comment.