diff --git a/app/src/main/java/chat/revolt/components/screens/chat/AttachmentManager.kt b/app/src/main/java/chat/revolt/components/screens/chat/AttachmentManager.kt index 0a9cf52c..cddbc6b7 100644 --- a/app/src/main/java/chat/revolt/components/screens/chat/AttachmentManager.kt +++ b/app/src/main/java/chat/revolt/components/screens/chat/AttachmentManager.kt @@ -1,15 +1,27 @@ package chat.revolt.components.screens.chat import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProgressIndicatorDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource @@ -23,44 +35,57 @@ import java.io.File fun AttachmentManager( attachments: List, uploading: Boolean, + uploadProgress: Float = 0f, onRemove: (FileArgs) -> Unit, ) { - Row( + val animatedProgress by animateFloatAsState( + targetValue = uploadProgress, + animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, + label = "Upload progress" + ) + + Column( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) - .horizontalScroll(rememberScrollState()) - .padding(horizontal = 8.dp, vertical = 4.dp) ) { - AnimatedVisibility(uploading) { - CircularProgressIndicator( - modifier = Modifier.padding(4.dp), - color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.7f) - ) + Row( + modifier = Modifier + .horizontalScroll(rememberScrollState()) + .padding(horizontal = 8.dp, vertical = 4.dp) + ) { + + attachments.forEach { attachment -> + Row( + modifier = Modifier + .padding(4.dp) + .clip(MaterialTheme.shapes.small) + .clickable { + onRemove(attachment) + } + .background( + color = MaterialTheme.colorScheme.background, + shape = MaterialTheme.shapes.small + ) + .padding(8.dp) + ) { + Text(attachment.filename, maxLines = 1) + Spacer(modifier = Modifier.width(4.dp)) + Icon( + Icons.Default.Close, + contentDescription = stringResource(R.string.remove_attachment_alt) + ) + } + Spacer(modifier = Modifier.width(8.dp)) + } } - attachments.forEach { attachment -> - Row( + AnimatedVisibility(visible = uploading) { + LinearProgressIndicator( + progress = animatedProgress, modifier = Modifier - .padding(4.dp) - .clip(MaterialTheme.shapes.small) - .clickable { - onRemove(attachment) - } - .background( - color = MaterialTheme.colorScheme.background, - shape = MaterialTheme.shapes.small - ) - .padding(8.dp) - ) { - Text(attachment.filename, maxLines = 1) - Spacer(modifier = Modifier.width(4.dp)) - Icon( - Icons.Default.Close, - contentDescription = stringResource(R.string.remove_attachment_alt) - ) - } - Spacer(modifier = Modifier.width(8.dp)) + .fillMaxWidth() + ) } } } diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt index b9e85f41..920b7060 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreen.kt @@ -385,7 +385,8 @@ fun ChannelScreen( AttachmentManager( attachments = viewModel.pendingAttachments, uploading = viewModel.isSendingMessage, - onRemove = { viewModel.pendingAttachments.remove(it) } + uploadProgress = viewModel.pendingUploadProgress, + onRemove = { viewModel.pendingAttachments.remove(it) }, ) } diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt index 821dc664..2d72cbc7 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt @@ -2,10 +2,11 @@ package chat.revolt.screens.chat.views.channel import android.util.Log import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.toMutableStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import chat.revolt.api.RevoltAPI @@ -55,9 +56,11 @@ class ChannelScreenViewModel : ViewModel() { var pendingReplies = mutableStateListOf() var pendingAttachments = mutableStateListOf() + var pendingUploadProgress by mutableFloatStateOf(0f) + private fun popAttachmentBatch() { pendingAttachments = - pendingAttachments.drop(MAX_ATTACHMENTS_PER_MESSAGE) as SnapshotStateList + pendingAttachments.drop(MAX_ATTACHMENTS_PER_MESSAGE).toMutableStateList() } private fun setRenderableMessages(messages: List) { @@ -142,14 +145,20 @@ class ChannelScreenViewModel : ViewModel() { viewModelScope.launch { val attachmentIds = arrayListOf() + val takenAttachments = pendingAttachments.take(MAX_ATTACHMENTS_PER_MESSAGE) + val totalTaken = takenAttachments.size - pendingAttachments.take(MAX_ATTACHMENTS_PER_MESSAGE).forEach { + takenAttachments.forEachIndexed { index, it -> try { val id = uploadToAutumn( it.file, it.filename, "attachments", - ContentType.parse(it.contentType) + ContentType.parse(it.contentType), + onProgress = { current, total -> + pendingUploadProgress = + ((current.toFloat() / total.toFloat()) / totalTaken.toFloat()) + (index.toFloat() / totalTaken.toFloat()) + } ) Log.d("ChannelScreen", "Uploaded attachment with id $id") attachmentIds.add(id) @@ -168,6 +177,8 @@ class ChannelScreenViewModel : ViewModel() { pendingMessageContent = "" hasNoMoreMessages = false + isSendingMessage = false + pendingUploadProgress = 0f popAttachmentBatch() clearInReplyTo() }