Skip to content

Commit 36a9b84

Browse files
committed
Reimplementing download indicator
Reimplementing openWhenDownloaded functionality Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
1 parent d780b6c commit 36a9b84

6 files changed

Lines changed: 148 additions & 38 deletions

File tree

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,16 @@ import androidx.cardview.widget.CardView
6464
import androidx.compose.foundation.lazy.LazyListState
6565
import androidx.compose.foundation.lazy.rememberLazyListState
6666
import androidx.compose.material3.MaterialTheme
67+
import androidx.compose.runtime.Composable
6768
import androidx.compose.runtime.CompositionLocalProvider
69+
import androidx.compose.runtime.LaunchedEffect
70+
import androidx.compose.runtime.MutableState
6871
import androidx.compose.runtime.SideEffect
72+
import androidx.compose.runtime.derivedStateOf
6973
import androidx.compose.runtime.getValue
7074
import androidx.compose.runtime.mutableStateOf
7175
import androidx.compose.runtime.produceState
76+
import androidx.compose.runtime.remember
7277
import androidx.compose.runtime.setValue
7378
import androidx.compose.ui.platform.ComposeView
7479
import androidx.coordinatorlayout.widget.CoordinatorLayout
@@ -117,6 +122,8 @@ import com.nextcloud.talk.api.NcApi
117122
import com.nextcloud.talk.api.NcApiCoroutines
118123
import com.nextcloud.talk.application.NextcloudTalkApplication
119124
import com.nextcloud.talk.chat.data.model.ChatMessage
125+
import com.nextcloud.talk.chat.data.model.FileParameters
126+
import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet
120127
import com.nextcloud.talk.chat.ui.model.MessageTypeContent
121128
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
122129
import com.nextcloud.talk.chat.viewmodels.MessageInputViewModel
@@ -162,7 +169,6 @@ import com.nextcloud.talk.ui.dialog.FileAttachmentPreviewFragment
162169
import com.nextcloud.talk.ui.dialog.GetPinnedOptionsDialog
163170
import com.nextcloud.talk.ui.dialog.MessageActionsDialog
164171
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
165-
import com.nextcloud.talk.chat.ui.ShowReactionsModalBottomSheet
166172
import com.nextcloud.talk.ui.dialog.TempMessageActionsDialog
167173
import com.nextcloud.talk.ui.theme.LocalMessageUtils
168174
import com.nextcloud.talk.ui.theme.LocalOpenGraphFetcher
@@ -603,6 +609,7 @@ class ChatActivity :
603609
binding.messagesListViewCompose.visibility = View.VISIBLE
604610

605611
val listState = rememberLazyListState()
612+
606613
SideEffect { chatListState = listState }
607614

608615
CompositionLocalProvider(
@@ -613,21 +620,34 @@ class ChatActivity :
613620
val isOneToOneConversation = uiState.isOneToOneConversation
614621
Log.d(TAG, "isOneToOneConversation=" + isOneToOneConversation)
615622

623+
// list of the file ids of messages being downloaded
624+
val downloadingFileState = remember { mutableStateOf(listOf<String>()) }
625+
626+
// openWhenDownloaded is a derived boolean state of the visible chat message list on the condition
627+
// that if any of the messages that are present contain a fileId that is within downloadingFileState
628+
val openWhenDownloadState = remember { mutableStateOf(false) }
629+
630+
val visibleIds = listState.visibleItemsWithThreshold()
631+
LaunchedEffect(visibleIds, downloadingFileState.value) {
632+
openWhenDownloadState.value = (downloadingFileState.value.intersect(visibleIds).isNotEmpty())
633+
}
634+
616635
ChatView(
617636
state = ChatViewState(
618637
chatItems = uiState.items,
619638
isOneToOneConversation = isOneToOneConversation,
620639
conversationThreadId = conversationThreadId,
621640
hasChatPermission = this::participantPermissions.isInitialized &&
622-
participantPermissions.hasChatPermission()
641+
participantPermissions.hasChatPermission(),
642+
downloadingFileState = downloadingFileState.value
623643
),
624644
callbacks = ChatViewCallbacks(
625645
onLoadMore = { loadMoreMessagesCompose() },
626646
advanceLocalLastReadMessageIfNeeded = { advanceLocalLastReadMessageIfNeeded(it) },
627647
updateRemoteLastReadMessageIfNeeded = { updateRemoteLastReadMessageIfNeeded() },
628648
onLongClick = { openMessageActionsDialog(it) },
629649
onSwipeReply = { handleSwipeToReply(it) },
630-
onFileClick = { downloadAndOpenFile(it) },
650+
onFileClick = { downloadAndOpenFile(it, openWhenDownloadState, downloadingFileState) },
631651
onPollClick = { pollId, pollName -> openPollDialog(pollId, pollName) },
632652
onVoicePlayPauseClick = { onVoicePlayPauseClickCompose(it) },
633653
onVoiceSeek = { _, progress -> chatViewModel.seekToMediaPlayer(progress) },
@@ -662,6 +682,39 @@ class ChatActivity :
662682
}
663683
}
664684

685+
@Composable
686+
private fun LazyListState.visibleItemsWithThreshold(): List<String> =
687+
remember(this) {
688+
derivedStateOf {
689+
val visibleItemsInfo = layoutInfo.visibleItemsInfo
690+
if (layoutInfo.totalItemsCount == 0) {
691+
emptyList()
692+
} else {
693+
visibleItemsInfo.toMutableList().map { it.key as String }
694+
}
695+
}
696+
}.value.mapNotNull { key ->
697+
val messageItem = chatViewModel.uiState.value.items.firstOrNull { it.stableKey() == key }
698+
val message = messageItem?.messageOrNull()
699+
var result: String? = null
700+
message?.let {
701+
if (message.messageParameters.isNotEmpty()) {
702+
runCatching {
703+
message.messageParameters as HashMap<String?, HashMap<String?, String?>>?
704+
val fileParameters = FileParameters(message.messageParameters)
705+
result = fileParameters.id
706+
}.onFailure { e ->
707+
when (e) {
708+
is ClassCastException -> {} // weird
709+
else -> Log.e(TAG, "Error in LazyListState.visibleItemsWithThreshold $e")
710+
}
711+
}
712+
}
713+
}
714+
715+
result
716+
}
717+
665718
private fun onLoadQuotedMessage(messageId: Int) {
666719
// Loading and displaying surrounding messages for quotes is pending; replace flow from latestChatBlock with
667720
// other flow
@@ -730,10 +783,18 @@ class ChatActivity :
730783
chatViewModel.setVoiceMessageSpeed(messageId, nextSpeed)
731784
}
732785

733-
fun downloadAndOpenFile(messageId: Int) {
786+
fun downloadAndOpenFile(
787+
messageId: Int,
788+
openWhenDownloadState: MutableState<Boolean>,
789+
downloadState: MutableState<List<String>>
790+
) {
734791
lifecycleScope.launch {
735792
val chatMessage = chatViewModel.getMessageById(messageId.toLong()).first()
736-
FileViewerUtils(this@ChatActivity, conversationUser).openFile(chatMessage)
793+
FileViewerUtils(this@ChatActivity, conversationUser).openFile(
794+
chatMessage,
795+
openWhenDownloadState,
796+
downloadState
797+
)
737798
}
738799
}
739800

@@ -3558,7 +3619,6 @@ class ChatActivity :
35583619

35593620
if (noteToSelfConversation != null) {
35603621
var shareUri: Uri? = null
3561-
val data: HashMap<String, String>?
35623622
var metaData = ""
35633623
var objectId = ""
35643624
if (message.hasFileAttachment) {

app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.content.Context
1111
import android.view.View
1212
import android.widget.ImageView
1313
import android.widget.ProgressBar
14+
import androidx.compose.runtime.mutableStateOf
1415
import androidx.recyclerview.widget.RecyclerView
1516
import androidx.viewbinding.ViewBinding
1617
import com.nextcloud.talk.data.user.model.User
@@ -58,6 +59,7 @@ abstract class SharedItemsViewHolder(
5859
This should be done after a refactoring of FileViewerUtils.
5960
*/
6061
val fileViewerUtils = FileViewerUtils(image.context, user)
62+
val trueState = mutableStateOf(true)
6163

6264
clickTarget.setOnClickListener {
6365
fileViewerUtils.openFile(
@@ -74,15 +76,15 @@ abstract class SharedItemsViewHolder(
7476
// null,
7577
// image
7678
// ),
77-
true
79+
trueState
7880
)
7981
}
8082

8183
fileViewerUtils.resumeToUpdateViewsByProgress(
8284
item.name,
8385
item.id,
8486
item.mimeType,
85-
true,
87+
trueState,
8688
FileViewerUtils.ProgressUi(progressBar, null, image)
8789
)
8890
}

app/src/main/java/com/nextcloud/talk/ui/chat/ChatMessageView.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ private const val QUOTE_HIGHLIGHT_FADE_OUT_MILLIS = 1500
5151
data class ChatMessageContext(
5252
val isOneToOneConversation: Boolean = false,
5353
val conversationThreadId: Long? = null,
54-
val hasChatPermission: Boolean = true
54+
val hasChatPermission: Boolean = true,
55+
val downloadingFileState: List<String> = listOf()
5556
)
5657

57-
class ChatMessageCallbacks(
58+
data class ChatMessageCallbacks(
5859
val onLongClick: ((Int) -> Unit?)? = null,
5960
val onSwipeReply: ((Int) -> Unit)? = null,
6061
val onFileClick: (Int) -> Unit = {},
@@ -68,6 +69,7 @@ class ChatMessageCallbacks(
6869
val onQuotedMessageClick: (Int) -> Unit = {}
6970
)
7071

72+
@Suppress("TooLongMethod")
7173
@Composable
7274
fun ChatMessageView(
7375
message: ChatMessageUi,
@@ -130,6 +132,7 @@ fun ChatMessageView(
130132
message = message,
131133
isOneToOneConversation = context.isOneToOneConversation,
132134
conversationThreadId = context.conversationThreadId,
135+
chatViewDownloadingFileState = context.downloadingFileState,
133136
onImageClick = callbacks.onFileClick
134137
)
135138
}

app/src/main/java/com/nextcloud/talk/ui/chat/ChatView.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import androidx.compose.animation.fadeOut
1515
import androidx.compose.animation.scaleIn
1616
import androidx.compose.animation.scaleOut
1717
import androidx.compose.foundation.ExperimentalFoundationApi
18-
import androidx.compose.foundation.gestures.animateScrollBy
1918
import androidx.compose.foundation.background
19+
import androidx.compose.foundation.gestures.animateScrollBy
2020
import androidx.compose.foundation.layout.Arrangement
2121
import androidx.compose.foundation.layout.Box
2222
import androidx.compose.foundation.layout.PaddingValues
@@ -25,8 +25,8 @@ import androidx.compose.foundation.layout.fillMaxSize
2525
import androidx.compose.foundation.layout.fillMaxWidth
2626
import androidx.compose.foundation.layout.padding
2727
import androidx.compose.foundation.layout.size
28-
import androidx.compose.foundation.lazy.LazyListState
2928
import androidx.compose.foundation.lazy.LazyColumn
29+
import androidx.compose.foundation.lazy.LazyListState
3030
import androidx.compose.foundation.lazy.items
3131
import androidx.compose.foundation.lazy.rememberLazyListState
3232
import androidx.compose.foundation.shape.CircleShape
@@ -57,6 +57,7 @@ import androidx.compose.ui.res.stringResource
5757
import androidx.compose.ui.tooling.preview.Preview
5858
import androidx.compose.ui.unit.dp
5959
import androidx.compose.ui.unit.sp
60+
import com.nextcloud.talk.R
6061
import com.nextcloud.talk.chat.ui.model.ChatMessageUi
6162
import com.nextcloud.talk.chat.ui.model.MessageStatusIcon
6263
import com.nextcloud.talk.chat.ui.model.MessageTypeContent
@@ -72,7 +73,6 @@ import java.time.Instant
7273
import java.time.LocalDate
7374
import java.time.ZoneId
7475
import java.time.format.DateTimeFormatter
75-
import com.nextcloud.talk.R
7676

7777
private const val LONG_1000 = 1000L
7878
private const val LOAD_MORE_BUFFER_ITEMS = 5
@@ -88,10 +88,11 @@ data class ChatViewState(
8888
val conversationThreadId: Long? = null,
8989
val hasChatPermission: Boolean = true,
9090
val initialUnreadCount: Int = 0,
91-
val initialShowUnreadPopup: Boolean = false
91+
val initialShowUnreadPopup: Boolean = false,
92+
val downloadingFileState: List<String> = listOf()
9293
)
9394

94-
class ChatViewCallbacks(
95+
data class ChatViewCallbacks(
9596
val onLoadMore: (() -> Unit?)? = null,
9697
val advanceLocalLastReadMessageIfNeeded: ((Int) -> Unit?)? = null,
9798
val updateRemoteLastReadMessageIfNeeded: (() -> Unit?)? = null,
@@ -311,7 +312,8 @@ fun ChatView(
311312
context = ChatMessageContext(
312313
isOneToOneConversation = state.isOneToOneConversation,
313314
conversationThreadId = state.conversationThreadId,
314-
hasChatPermission = state.hasChatPermission
315+
hasChatPermission = state.hasChatPermission,
316+
downloadingFileState = state.downloadingFileState
315317
),
316318
callbacks = ChatMessageCallbacks(
317319
onLongClick = callbacks.onLongClick,

app/src/main/java/com/nextcloud/talk/ui/chat/MediaMessage.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
1414
import androidx.compose.foundation.layout.padding
1515
import androidx.compose.foundation.layout.size
1616
import androidx.compose.foundation.shape.RoundedCornerShape
17+
import androidx.compose.material3.CircularProgressIndicator
1718
import androidx.compose.material3.Icon
1819
import androidx.compose.runtime.Composable
1920
import androidx.compose.runtime.remember
@@ -28,6 +29,7 @@ import androidx.compose.ui.res.stringResource
2829
import androidx.compose.ui.unit.dp
2930
import coil.compose.AsyncImage
3031
import com.nextcloud.talk.R
32+
import com.nextcloud.talk.chat.data.model.FileParameters
3133
import com.nextcloud.talk.chat.ui.model.ChatMessageUi
3234
import com.nextcloud.talk.chat.ui.model.MessageTypeContent
3335
import com.nextcloud.talk.contacts.load
@@ -37,15 +39,19 @@ private const val FILE_PLACEHOLDER_MESSAGE = "{file}"
3739
private val mediaRadiusBig = 8.dp
3840
private val mediaRadiusSmall = 2.dp
3941

40-
@Suppress("Detekt.LongMethod")
42+
@Suppress("Detekt.LongMethod", "LongParameterList")
4143
@Composable
4244
fun MediaMessage(
4345
typeContent: MessageTypeContent.Media,
4446
message: ChatMessageUi,
4547
isOneToOneConversation: Boolean = false,
4648
conversationThreadId: Long? = null,
49+
chatViewDownloadingFileState: List<String>,
4750
onImageClick: (Int) -> Unit
4851
) {
52+
val fileParameters =
53+
remember { FileParameters(message.messageParameters as HashMap<String?, HashMap<String?, String?>>?) }
54+
4955
val captionText = message.message.takeUnless { it == FILE_PLACEHOLDER_MESSAGE }
5056
val hasCaption = captionText != null
5157
val mediaInset = 4.dp
@@ -111,6 +117,15 @@ fun MediaMessage(
111117
tint = Color.White
112118
)
113119
}
120+
121+
if (chatViewDownloadingFileState.contains(fileParameters.id)) {
122+
CircularProgressIndicator(
123+
modifier = Modifier
124+
.size(48.dp)
125+
.align(Alignment.Center),
126+
strokeWidth = 2.dp
127+
)
128+
}
114129
}
115130
}
116131
}

0 commit comments

Comments
 (0)