Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Allow users to forward messages #468

Draft
wants to merge 5 commits into
base: feature/communication/forward-messages
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ object Spacings {
val addEmojiIconSize = 18.sp
}

object AutoCompletePopup {
object Popup {
val maxHeight = 270.dp

val HintHorizontalPadding = 16.dp
val ContentVerticalPadding = 8.dp

}


object CourseItem {
val height = 250.dp
val headerHeight = 70.dp
Expand Down
1 change: 1 addition & 0 deletions feature/metis/conversation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
testImplementation(project(":core:ui-test"))

implementation(project(":feature:metis:shared"))
implementation(project(":feature:metis:manage-conversations"))
testImplementation(project(":feature:metis-test"))

implementation(libs.androidx.paging.runtime)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service

import androidx.work.WorkContinuation
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.util.ForwardedSourcePostContent
import kotlinx.coroutines.flow.Flow

typealias CreatePostConfigurationBlock = WorkContinuation.(clientSidePostId: String) -> WorkContinuation
Expand All @@ -12,6 +13,8 @@ interface CreatePostService {
courseId: Long,
conversationId: Long,
content: String,
hasForwardedMessage: Boolean = false,
forwardedSourcePostList: List<ForwardedSourcePostContent>? = null,
configure: CreatePostConfigurationBlock = { this }
)

Expand All @@ -20,6 +23,8 @@ interface CreatePostService {
conversationId: Long,
clientSidePostId: String,
content: String,
hasForwardedMessage: Boolean = false,
forwardedSourcePostList: List<ForwardedSourcePostContent>? = null,
configure: CreatePostConfigurationBlock = { this }
)

Expand All @@ -28,6 +33,8 @@ interface CreatePostService {
conversationId: Long,
parentPostId: Long,
content: String,
hasForwardedMessage: Boolean = false,
forwardedSourcePostList: List<ForwardedSourcePostContent>? = null,
configure: CreatePostConfigurationBlock = { this }
)

Expand All @@ -37,6 +44,8 @@ interface CreatePostService {
parentPostId: Long,
clientSidePostId: String,
content: String,
hasForwardedMessage: Boolean = false,
forwardedSourcePostList: List<ForwardedSourcePostContent>? = null,
configure: CreatePostConfigurationBlock = { this }
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.work.WorkManager
import de.tum.informatics.www1.artemis.native_app.core.common.defaultInternetWorkRequest
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.CreatePostConfigurationBlock
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.CreatePostService
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.util.ForwardedSourcePostContent
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.work.BaseCreatePostWorker
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.work.CreateClientSidePostWorker
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.work.SendConversationPostWorker
Expand All @@ -22,6 +23,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
courseId: Long,
conversationId: Long,
content: String,
hasForwardedMessage: Boolean,
forwardedSourcePostList: List<ForwardedSourcePostContent>?,
configure: CreatePostConfigurationBlock
) {
scheduleCreatePostWork(
Expand All @@ -30,6 +33,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
content = content,
postType = BaseCreatePostWorker.PostType.POST,
parentPostId = null,
hasForwardedMessage = hasForwardedMessage,
forwardedSourcePostList = forwardedSourcePostList,
clientSidePostId = null,
configure = configure
)
Expand All @@ -40,6 +45,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
conversationId: Long,
parentPostId: Long,
content: String,
hasForwardedMessage: Boolean,
forwardedSourcePostList: List<ForwardedSourcePostContent>?,
configure: CreatePostConfigurationBlock
) {
scheduleCreatePostWork(
Expand All @@ -48,6 +55,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
content = content,
postType = BaseCreatePostWorker.PostType.ANSWER_POST,
parentPostId = parentPostId,
hasForwardedMessage = hasForwardedMessage,
forwardedSourcePostList = forwardedSourcePostList,
clientSidePostId = null,
configure = configure
)
Expand All @@ -58,6 +67,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
conversationId: Long,
clientSidePostId: String,
content: String,
hasForwardedMessage: Boolean,
forwardedSourcePostList: List<ForwardedSourcePostContent>?,
configure: CreatePostConfigurationBlock
) {
scheduleCreatePostWork(
Expand All @@ -66,6 +77,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
content = content,
postType = BaseCreatePostWorker.PostType.POST,
parentPostId = null,
hasForwardedMessage = hasForwardedMessage,
forwardedSourcePostList = forwardedSourcePostList,
clientSidePostId = clientSidePostId,
configure = configure
)
Expand All @@ -77,6 +90,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
parentPostId: Long,
clientSidePostId: String,
content: String,
hasForwardedMessage: Boolean,
forwardedSourcePostList: List<ForwardedSourcePostContent>?,
configure: CreatePostConfigurationBlock
) {
scheduleCreatePostWork(
Expand All @@ -86,6 +101,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
postType = BaseCreatePostWorker.PostType.POST,
parentPostId = parentPostId,
clientSidePostId = clientSidePostId,
hasForwardedMessage = hasForwardedMessage,
forwardedSourcePostList = forwardedSourcePostList,
configure = configure
)
}
Expand Down Expand Up @@ -113,6 +130,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
content: String,
postType: BaseCreatePostWorker.PostType,
parentPostId: Long?,
hasForwardedMessage: Boolean,
forwardedSourcePostList: List<ForwardedSourcePostContent>?,
clientSidePostId: String?,
configure: CreatePostConfigurationBlock
) {
Expand All @@ -124,6 +143,8 @@ internal class CreatePostServiceImpl(private val context: Context) : CreatePostS
clientSidePostId = postId,
content = content,
postType = postType,
hasForwardedMessage = hasForwardedMessage,
forwardedSourcePostList = forwardedSourcePostList,
parentPostId = parentPostId
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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.ForwardedMessage
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.PostingType
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost
import io.ktor.client.statement.HttpResponse

interface MetisService {

Expand Down Expand Up @@ -42,6 +43,13 @@ interface MetisService {
authToken: String
): NetworkResponse<List<ForwardedMessage>>

suspend fun createForwardedMessage(
metisContext: MetisContext,
forwardedMessage: ForwardedMessage,
serverUrl: String,
authToken: String
): NetworkResponse<HttpResponse>

suspend fun getPostsByIds(
metisContext: MetisContext,
postIds: List<Long>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.d
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType
import io.ktor.http.appendPathSegments
import io.ktor.http.contentType
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -208,6 +213,26 @@ internal class MetisServiceImpl(
}
}

override suspend fun createForwardedMessage(
metisContext: MetisContext,
forwardedMessage: ForwardedMessage,
serverUrl: String,
authToken: String
): NetworkResponse<HttpResponse> {
return performNetworkCall {
ktorProvider.ktorClient.post(serverUrl) {
url {
appendPathSegments("api", "communication", "forwarded-messages")
}
parameter("courseId", metisContext.courseId.toString())

contentType(ContentType.Application.Json)
setBody(forwardedMessage)
cookieAuth(authToken)
}
}
}

override suspend fun getPostsByIds(
metisContext: MetisContext,
postIds: List<Long>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.widget.Toast
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.ui.text.input.TextFieldValue
import androidx.core.content.ContextCompat.getString
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import de.tum.informatics.www1.artemis.native_app.core.common.flatMapLatest
import de.tum.informatics.www1.artemis.native_app.core.data.DataState
Expand Down Expand Up @@ -41,6 +42,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ser
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.ReplyTextStorageService
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.chatlist.ConversationChatListUseCase
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.post_actions.PostActionFlags
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.util.ForwardMessageUseCase
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.util.LinkPreviewUtil
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.InitialReplyTextProvider
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.autocomplete.AutoCompletionUseCase
Expand Down Expand Up @@ -198,6 +200,19 @@ internal open class ConversationViewModel(
coroutineContext = coroutineContext
)

val forwardMessageUseCase = ForwardMessageUseCase(
courseId = courseId,
conversationService = conversationService,
accountService = accountService,
metisService = metisService,
createPostService = createPostService,
serverConfigurationService = serverConfigurationService,
networkStatusProvider = networkStatusProvider,
savedStateHandle = SavedStateHandle(),
coroutineContext = coroutineContext,
onFileSelected = ::onFileSelected
)

/**
* Manages updating from the websocket.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,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.post.post_actions.PostActionFlags
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.post_actions.rememberPostActions
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.shouldDisplayHeader
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.util.ForwardMessageUseCase
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.InitialReplyTextProvider
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.MetisReplyHandler
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.ReplyTextField
Expand Down Expand Up @@ -82,6 +83,7 @@ internal fun MetisChatList(

val clientId: Long by viewModel.clientIdOrDefault.collectAsState()
val postActionFlags by viewModel.postActionFlags.collectAsState()
val forwardMessageUseCase = viewModel.forwardMessageUseCase

val serverUrl by viewModel.serverUrl.collectAsState()

Expand All @@ -105,6 +107,7 @@ internal fun MetisChatList(
state = state,
isMarkedAsDeleteList = viewModel.isMarkedAsDeleteList,
bottomItem = bottomItem,
forwardMessageUseCase = forwardMessageUseCase,
isReplyEnabled = isReplyEnabled,
onCreatePost = viewModel::createPost,
onEditPost = viewModel::editPost,
Expand Down Expand Up @@ -133,6 +136,7 @@ fun MetisChatList(
bottomItem: PostPojo?,
clientId: Long,
postActionFlags: PostActionFlags,
forwardMessageUseCase: ForwardMessageUseCase = koinInject(),
serverUrl: String,
courseId: Long,
state: LazyListState,
Expand Down Expand Up @@ -209,6 +213,7 @@ fun MetisChatList(
isMarkedAsDeleteList = isMarkedAsDeleteList,
onClickViewPost = onClickViewPost,
postActionFlags = postActionFlags,
forwardMessageUseCase = forwardMessageUseCase,
onRequestEdit = onEditPostDelegate,
onRequestDelete = onDeletePostDelegate,
onRequestUndoDelete = onUndoDeletePost,
Expand Down Expand Up @@ -243,6 +248,7 @@ private fun ChatList(
state: LazyListState,
posts: PostsDataState.Loaded,
postActionFlags: PostActionFlags,
forwardMessageUseCase: ForwardMessageUseCase,
isMarkedAsDeleteList: SnapshotStateList<IBasePost>,
clientId: Long,
displayUnreadIndicator: Boolean = false, // See https://github.com/ls1intum/artemis-android/pull/375#issuecomment-2656030353
Expand Down Expand Up @@ -309,6 +315,7 @@ private fun ChatList(
},
onResolvePost = null,
onPinPost = { onRequestPin(post) },
onForwardPost = null, // Set in PostWithBottomSheet
onSavePost = { onRequestSave(post) },
onRequestRetrySend = {
onRequestRetrySend(
Expand All @@ -328,6 +335,7 @@ private fun ChatList(
chatListItem = chatListItem,
postActions = postActions,
linkPreviews = linkPreviews,
forwardMessageUseCase = forwardMessageUseCase,
displayHeader = shouldDisplayHeader(
index = index,
post = post,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ private fun ForwardedMessageItem(
modifier: Modifier,
courseId: Long,
forwardedPost: IBasePost?,
isPreview: Boolean = false,
) {
QuotedMessageContainer(
modifier = modifier
Expand All @@ -72,11 +73,13 @@ private fun ForwardedMessageItem(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(Spacings.Post.innerSpacing)
) {
ForwardedMessageHeader(
modifier = Modifier,
forwardedPost = forwardedPost,
courseId = courseId,
)
if (!isPreview) {
ForwardedMessageHeader(
modifier = Modifier,
forwardedPost = forwardedPost,
courseId = courseId,
)
}

if (forwardedPost == null) {
Text(
Expand Down Expand Up @@ -148,6 +151,19 @@ private fun ForwardedMessageHeader(
}
}

@Composable
fun ForwardedMessagePreview(
modifier: Modifier,
forwardedPost: IBasePost?
) {
ForwardedMessageItem(
modifier = modifier,
courseId = -1,
forwardedPost = forwardedPost,
isPreview = true
)
}

@Composable
private fun resolveConversation(forwardedPost: IBasePost?): Triple<Long?, String, ConversationType> {
val conversation = when (forwardedPost) {
Expand Down
Loading
Loading