Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
3b353bd
feat: new loading behavior
ohassine Apr 23, 2026
6704b01
Merge remote-tracking branch 'origin/develop' into new-loading-behavior
ohassine Apr 24, 2026
5d97f06
Merge remote-tracking branch 'origin/develop' into new-loading-behavior
ohassine Apr 24, 2026
43c0418
feat: remove download to external storage option
ohassine Apr 27, 2026
9ea06e7
feat: cleanup
ohassine Apr 28, 2026
fa81683
feat: detekt
ohassine Apr 28, 2026
dcf676f
Merge remote-tracking branch 'origin/develop' into new-loading-behavior
ohassine Apr 28, 2026
ca33415
Merge remote-tracking branch 'origin/develop' into new-loading-behavior
ohassine Apr 28, 2026
ff7f3b5
feat: cleanup
ohassine Apr 28, 2026
8d57c00
feat: cleanup
ohassine Apr 28, 2026
5296036
feat: cleanup
ohassine Apr 28, 2026
add26ee
fix: cleanup
ohassine Apr 29, 2026
942a6e2
fix: cleanup
ohassine Apr 29, 2026
a9e6d8c
feat: refactor
ohassine Apr 29, 2026
2bb96f2
feat: unit test
ohassine Apr 29, 2026
d403848
Merge branch 'develop' into new-loading-behavior
ohassine Apr 29, 2026
771b73d
feat: detekt
ohassine Apr 29, 2026
3f458b1
Merge remote-tracking branch 'origin/new-loading-behavior' into new-l…
ohassine Apr 30, 2026
784c3d3
feat: detekt
ohassine Apr 30, 2026
24a0eea
Merge branch 'develop' into new-loading-behavior
ohassine Apr 30, 2026
f8a0d11
Merge branch 'develop' into new-loading-behavior
ohassine May 4, 2026
d76da05
chore: test
ohassine May 4, 2026
1a25574
Merge branch 'develop' into new-loading-behavior
ohassine May 4, 2026
defa808
feat(drive): Make files available offline
ohassine May 6, 2026
57eb842
Merge remote-tracking branch 'origin/develop' into Make-files-availab…
ohassine May 6, 2026
ed03ad9
feat(drive): Make files available offline
ohassine May 7, 2026
e45ce22
Merge remote-tracking branch 'origin/develop' into Make-files-availab…
ohassine May 7, 2026
e629614
chore: cleanup
ohassine May 7, 2026
a6852d9
chore: cleanup
ohassine May 7, 2026
5db198b
chore: detekt
ohassine May 7, 2026
2a1bd5f
chore: detekt
ohassine May 8, 2026
fcd8bb1
chore: cleanup
ohassine May 8, 2026
e7589b7
Merge remote-tracking branch 'origin/develop' into Make-files-availab…
ohassine May 8, 2026
66771b7
feat: modifiedAt from String to Long
ohassine May 12, 2026
6576a50
feat: cleanup
ohassine May 12, 2026
6343f92
Merge branch 'develop' into Make-files-available-offline
ohassine May 12, 2026
c4c8be5
feat: cleanup
ohassine May 12, 2026
22fd15f
feat: kalium reference
ohassine May 12, 2026
d050772
feat: cleanup
ohassine May 12, 2026
d2d0998
feat: test
ohassine May 12, 2026
b9ff6a8
Merge branch 'develop' into Make-files-available-offline
ohassine May 12, 2026
12e6365
fix: incorrect icon usage
ohassine May 12, 2026
a30de5a
Merge remote-tracking branch 'origin/Make-files-available-offline' in…
ohassine May 12, 2026
204ae28
fix: detekt
ohassine May 12, 2026
bd1a0df
fix: lint
ohassine May 12, 2026
15042f2
feat: display offline files
ohassine May 12, 2026
0d713bc
feat: display offline files
ohassine May 13, 2026
c5225a5
Merge remote-tracking branch 'origin/Make-files-available-offline' in…
ohassine May 13, 2026
a334c17
feat: display offline files
ohassine May 13, 2026
af93b1d
feat: cleanup
ohassine May 13, 2026
b59a08f
feat: cleanup
ohassine May 13, 2026
c51175a
feat: support offline mode in search screen
ohassine May 13, 2026
389525d
chore: cleanup
ohassine May 13, 2026
6489c30
chore: kalium
ohassine May 13, 2026
11509a2
Merge remote-tracking branch 'origin/develop' into display-offline-files
ohassine May 13, 2026
952a1ae
chore: lint
ohassine May 13, 2026
1ebbcbd
Merge remote-tracking branch 'origin/develop' into Make-files-availab…
ohassine May 13, 2026
bb0cda9
Merge branch 'Make-files-available-offline' into display-offline-files
ohassine May 13, 2026
785f817
chore: cleanup
ohassine May 13, 2026
065917d
chore: add conversationId
ohassine May 13, 2026
bea8de1
chore: add conversationId
ohassine May 13, 2026
9cc0b51
chore: kalium reference
ohassine May 13, 2026
b9cdfef
chore: test
ohassine May 13, 2026
e4aadf2
chore: detekt
ohassine May 13, 2026
3cc3b57
Merge branch 'develop' into Make-files-available-offline
ohassine May 13, 2026
155c2d1
Merge remote-tracking branch 'origin/Make-files-available-offline' in…
ohassine May 13, 2026
0d26300
Merge branch 'Make-files-available-offline' into display-offline-files
ohassine May 13, 2026
3965331
chore: detekt
ohassine May 13, 2026
6125be1
chore: kalium
ohassine May 13, 2026
27f686e
chore: test
ohassine May 13, 2026
e44bd6f
Merge branch 'Make-files-available-offline' into display-offline-files
ohassine May 13, 2026
d4c75c1
chore: test
ohassine May 13, 2026
8c63ab4
chore: detekt
ohassine May 13, 2026
c69e635
Merge remote-tracking branch 'origin/Make-files-available-offline' in…
ohassine May 13, 2026
92c9caa
chore: cleanup
ohassine May 13, 2026
7bedbad
refactor: add mimeType
ohassine May 18, 2026
dffb611
feat: show offline indicator for files in conversation
ohassine May 18, 2026
702224a
feat: loading files in conversation
ohassine Jun 15, 2026
4e3811f
Merge remote-tracking branch 'origin/develop' into loading-files-in-c…
ohassine Jun 15, 2026
db911e2
Merge remote-tracking branch 'origin/develop' into loading-files-in-c…
ohassine Jul 17, 2026
8f6fe1a
feat: load video
ohassine Jul 17, 2026
8724f4d
Merge remote-tracking branch 'origin/develop' into loading-files-in-c…
ohassine Jul 27, 2026
8d1bd0f
Merge remote-tracking branch 'origin/develop' into loading-files-in-c…
ohassine Sep 8, 2026
1f587f3
chore: cleanup
ohassine Sep 8, 2026
7d6f7ba
chore: detekt
ohassine Sep 8, 2026
5299e16
chore: detekt
ohassine Sep 8, 2026
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 @@ -18,6 +18,7 @@
package com.wire.android.ui.common.attachmentdraft.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down Expand Up @@ -57,6 +58,8 @@ fun FileHeaderView(
label: String? = null,
labelColor: Color? = null,
isError: Boolean = false,
showLabelInBubble: Boolean = false,
leadingContent: (@Composable () -> Unit)? = null,
) {
val context = LocalContext.current
val attachmentFileType = type ?: remember(extension) { AttachmentFileType.fromExtension(extension) }
Expand All @@ -67,31 +70,37 @@ fun FileHeaderView(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensions().spacing4x)
) {
if (isError) {
Icon(
modifier = Modifier.size(dimensions().spacing16x),
painter = painterResource(R.drawable.ic_warning_amber),
tint = colorsScheme().error,
contentDescription = null,
)
if (leadingContent != null) {
Box(modifier = Modifier.size(dimensions().spacing16x), contentAlignment = Alignment.Center) {
leadingContent()
}
} else {
Image(
modifier = Modifier.size(dimensions().spacing16x),
painter = painterResource(id = attachmentFileType.icon()),
contentDescription = null,
)
if (isError) {
Icon(
modifier = Modifier.size(dimensions().spacing16x),
painter = painterResource(R.drawable.ic_warning_amber),
tint = colorsScheme().error,
contentDescription = null,
)
} else {
Image(
modifier = Modifier.size(dimensions().spacing16x),
painter = painterResource(id = attachmentFileType.icon()),
contentDescription = null,
)
}
}
Text(
text = "${extension.uppercase()} ($sizeString)",
style = typography().subline01,
color = messageStyle.textColor(),
)
Spacer(modifier = Modifier.weight(1f))
if (!messageStyle.isBubble()) {
if (!messageStyle.isBubble() || showLabelInBubble) {
label?.let {
Text(
text = label,
style = typography().body02,
style = typography().subline01,
color = labelColor ?: messageStyle.textColor(),
)
}
Expand All @@ -116,13 +125,11 @@ private fun PreviewFileHeader() {
FileHeaderView(
extension = "DOCX",
size = 6796203,
label = "Downloading...",
messageStyle = MessageStyle.NORMAL
)
FileHeaderView(
extension = "ZIP",
size = 512746,
label = "Tap to view",
messageStyle = MessageStyle.NORMAL
)
FileHeaderView(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@ data class MultipartAttachmentUi(
val progress: Float? = null,
val isEditSupported: Boolean = false,
val isAvailableOffline: Boolean = false,
val openLoadState: MultipartAttachmentOpenLoadState? = null,
)

sealed interface MultipartAttachmentOpenLoadState {
data class Loading(val progress: Float? = null) : MultipartAttachmentOpenLoadState
data class Ready(val localPath: String) : MultipartAttachmentOpenLoadState
data object Error : MultipartAttachmentOpenLoadState
}

enum class AssetSource {
CELL, ASSET_STORAGE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
package com.wire.android.ui.home.conversations.model.messagetypes.multipart

import android.content.res.Configuration
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onVisibilityChanged
import androidx.compose.ui.platform.LocalConfiguration
Expand All @@ -38,15 +38,18 @@ import coil3.request.ImageRequest
import coil3.request.crossfade
import com.wire.android.ui.common.colorsScheme
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.multipart.MultipartAttachmentOpenLoadState
import com.wire.android.ui.common.multipart.MultipartAttachmentUi
import com.wire.android.ui.home.conversations.multipartAttachmentsViewModel
import com.wire.android.ui.common.multipart.toUiModel
import com.wire.android.ui.home.conversations.messages.item.MessageStyle
import com.wire.android.ui.home.conversations.model.messagetypes.multipart.grid.AssetGridPreview
import com.wire.android.ui.home.conversations.model.messagetypes.multipart.standalone.AssetPreview
import com.wire.android.ui.home.conversations.multipartAttachmentsViewModel
import com.wire.kalium.logic.data.asset.AssetTransferStatus
import com.wire.kalium.logic.data.asset.isFailed
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.MessageAttachment
import com.wire.android.feature.cells.R as cellsR

/**
* Displays a list of message attachments as a grid or a single attachment card.
Expand All @@ -66,17 +69,30 @@ fun MultipartAttachmentsView(
else -> multipartAttachmentsViewModel(conversationId)
}
) {
// Collect to trigger recomposition when offline availability changes.
val offlineAttachmentIds by viewModel.offlineAttachmentIds.collectAsStateWithLifecycle()
val context = LocalContext.current
val offlineAttachmentIds = viewModel.offlineAttachmentIds.collectAsStateWithLifecycle().value
val openLoadStates = viewModel.openLoadStates.collectAsStateWithLifecycle().value

LaunchedEffect(viewModel) {
viewModel.openAttachmentErrorEvent.collect {
Toast.makeText(context, cellsR.string.no_app_found, Toast.LENGTH_SHORT).show()
}
}

// TODO I found out that empty attachments list is not handled here and it shows empty message with no information
if (attachments.size == 1) {
val attachment = attachments.first()
val item = remember(attachment, offlineAttachmentIds) {
viewModel.mapAttachment(attachment)
}
item.let {
AssetPreview(
val openLoadState = openLoadStates[attachment.assetId()]
val loadingProgress = (openLoadState as? MultipartAttachmentOpenLoadState.Loading)?.progress
val readyLocalPath = (openLoadState as? MultipartAttachmentOpenLoadState.Ready)?.localPath
val uiModel = attachment.toUiModel(
progress = loadingProgress,
isAvailableOffline = attachment.assetId() in offlineAttachmentIds,
).copy(
openLoadState = openLoadState,
localPath = readyLocalPath ?: attachment.toUiModel().localPath,
)
AssetPreview(
modifier = modifier
.onVisibilityChanged { visible ->
if (visible) {
Expand All @@ -85,11 +101,11 @@ fun MultipartAttachmentsView(
viewModel.onAttachmentsHidden(attachments)
}
},
item = it,
item = uiModel,
messageStyle = messageStyle,
onClick = {
viewModel.onClick(
attachment = it,
attachment = uiModel,
openInImageViewer = onImageAttachmentClick,
openInVideoPlayer = { att ->
onVideoAttachmentClick(att.localPath, att.contentUrl, att.fileName)
Expand All @@ -100,11 +116,12 @@ fun MultipartAttachmentsView(
)
},
)
}
} else {
val groups = remember(attachments, offlineAttachmentIds) {
viewModel.mapAttachments(attachments = attachments)
}
val groups = viewModel.mapAttachments(
attachments = attachments,
offlineAttachmentIds = offlineAttachmentIds,
openLoadStates = openLoadStates,
)

Column(
modifier = modifier
Expand Down
Loading
Loading