Render caption below audio and file Event in the timeline.
This commit is contained in:
parent
ece62b7978
commit
f353ecdd45
5 changed files with 155 additions and 95 deletions
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.timeline.components.event
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout
|
||||||
|
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* package-private, you should only use TimelineItemFileView and TimelineItemAudioView.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun TimelineItemAttachmentView(
|
||||||
|
filename: String,
|
||||||
|
fileExtensionAndSize: String,
|
||||||
|
caption: String?,
|
||||||
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
icon: (@Composable () -> Unit) = {},
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
TimelineItemAttachmentHeaderView(
|
||||||
|
filename = filename,
|
||||||
|
fileExtensionAndSize = fileExtensionAndSize,
|
||||||
|
hasCaption = caption != null,
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
|
icon = icon,
|
||||||
|
)
|
||||||
|
if (caption != null) {
|
||||||
|
TimelineItemAttachmentCaptionView(
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
caption = caption,
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TimelineItemAttachmentHeaderView(
|
||||||
|
filename: String,
|
||||||
|
fileExtensionAndSize: String,
|
||||||
|
hasCaption: Boolean,
|
||||||
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
icon: (@Composable () -> Unit),
|
||||||
|
) {
|
||||||
|
val iconSize = 32.dp
|
||||||
|
val spacing = 8.dp
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(iconSize)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(ElementTheme.materialColors.background),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
icon()
|
||||||
|
}
|
||||||
|
Spacer(Modifier.width(spacing))
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = filename,
|
||||||
|
color = ElementTheme.materialColors.primary,
|
||||||
|
maxLines = 2,
|
||||||
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = fileExtensionAndSize,
|
||||||
|
color = ElementTheme.materialColors.secondary,
|
||||||
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
onTextLayout = if (hasCaption) {
|
||||||
|
{}
|
||||||
|
} else {
|
||||||
|
ContentAvoidingLayout.measureLastTextLine(
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
|
extraWidth = iconSize + spacing
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TimelineItemAttachmentCaptionView(
|
||||||
|
caption: String,
|
||||||
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = modifier,
|
||||||
|
text = caption,
|
||||||
|
color = ElementTheme.materialColors.primary,
|
||||||
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
|
onTextLayout = ContentAvoidingLayout.measureLastTextLine(
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -7,32 +7,20 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.components.event
|
package io.element.android.features.messages.impl.timeline.components.event
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.GraphicEq
|
import androidx.compose.material.icons.outlined.GraphicEq
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout
|
|
||||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContentProvider
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemAudioView(
|
fun TimelineItemAudioView(
|
||||||
|
|
@ -40,18 +28,13 @@ fun TimelineItemAudioView(
|
||||||
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val iconSize = 32.dp
|
TimelineItemAttachmentView(
|
||||||
val spacing = 8.dp
|
filename = content.filename,
|
||||||
Row(
|
fileExtensionAndSize = content.fileExtensionAndSize,
|
||||||
|
caption = content.caption,
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
icon = {
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(iconSize)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(ElementTheme.materialColors.background),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.GraphicEq,
|
imageVector = Icons.Outlined.GraphicEq,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -60,28 +43,7 @@ fun TimelineItemAudioView(
|
||||||
.size(16.dp),
|
.size(16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(spacing))
|
)
|
||||||
Column {
|
|
||||||
Text(
|
|
||||||
text = content.bestDescription,
|
|
||||||
color = ElementTheme.materialColors.primary,
|
|
||||||
maxLines = 2,
|
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = content.fileExtensionAndSize,
|
|
||||||
color = ElementTheme.materialColors.secondary,
|
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
onTextLayout = ContentAvoidingLayout.measureLastTextLine(
|
|
||||||
onContentLayoutChange = onContentLayoutChange,
|
|
||||||
extraWidth = iconSize + spacing
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -7,24 +7,13 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.components.event
|
package io.element.android.features.messages.impl.timeline.components.event
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout
|
|
||||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContentProvider
|
||||||
|
|
@ -32,7 +21,6 @@ import io.element.android.libraries.designsystem.icons.CompoundDrawables
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemFileView(
|
fun TimelineItemFileView(
|
||||||
|
|
@ -40,18 +28,13 @@ fun TimelineItemFileView(
|
||||||
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val iconSize = 32.dp
|
TimelineItemAttachmentView(
|
||||||
val spacing = 8.dp
|
filename = content.filename,
|
||||||
Row(
|
fileExtensionAndSize = content.fileExtensionAndSize,
|
||||||
|
caption = content.caption,
|
||||||
|
onContentLayoutChange = onContentLayoutChange,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
icon = {
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(iconSize)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(ElementTheme.materialColors.background),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Icon(
|
Icon(
|
||||||
resourceId = CompoundDrawables.ic_compound_attachment,
|
resourceId = CompoundDrawables.ic_compound_attachment,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|
@ -61,28 +44,7 @@ fun TimelineItemFileView(
|
||||||
.rotate(-45f),
|
.rotate(-45f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(spacing))
|
)
|
||||||
Column {
|
|
||||||
Text(
|
|
||||||
text = content.bestDescription,
|
|
||||||
color = ElementTheme.materialColors.primary,
|
|
||||||
maxLines = 2,
|
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = content.fileExtensionAndSize,
|
|
||||||
color = ElementTheme.materialColors.secondary,
|
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
onTextLayout = ContentAvoidingLayout.measureLastTextLine(
|
|
||||||
onContentLayoutChange = onContentLayoutChange,
|
|
||||||
extraWidth = iconSize + spacing
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,18 @@ open class TimelineItemAudioContentProvider : PreviewParameterProvider<TimelineI
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aTimelineItemAudioContent("A sound.mp3"),
|
aTimelineItemAudioContent("A sound.mp3"),
|
||||||
aTimelineItemAudioContent("A bigger name sound.mp3"),
|
aTimelineItemAudioContent("A bigger name sound.mp3"),
|
||||||
aTimelineItemAudioContent("An even bigger bigger bigger bigger bigger bigger bigger sound name which doesn't fit .mp3"),
|
aTimelineItemAudioContent("An even bigger bigger bigger bigger bigger bigger bigger sound name which doesn't fit.mp3"),
|
||||||
|
aTimelineItemAudioContent(caption = "A caption"),
|
||||||
|
aTimelineItemAudioContent(caption = "An even bigger bigger bigger bigger bigger bigger bigger caption"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aTimelineItemAudioContent(fileName: String = "A sound.mp3") = TimelineItemAudioContent(
|
fun aTimelineItemAudioContent(
|
||||||
|
fileName: String = "A sound.mp3",
|
||||||
|
caption: String? = null,
|
||||||
|
) = TimelineItemAudioContent(
|
||||||
filename = fileName,
|
filename = fileName,
|
||||||
caption = null,
|
caption = caption,
|
||||||
formattedCaption = null,
|
formattedCaption = null,
|
||||||
isEdited = false,
|
isEdited = false,
|
||||||
mimeType = MimeTypes.Mp3,
|
mimeType = MimeTypes.Mp3,
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,18 @@ open class TimelineItemFileContentProvider : PreviewParameterProvider<TimelineIt
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aTimelineItemFileContent(),
|
aTimelineItemFileContent(),
|
||||||
aTimelineItemFileContent("A bigger name file.pdf"),
|
aTimelineItemFileContent("A bigger name file.pdf"),
|
||||||
aTimelineItemFileContent("An even bigger bigger bigger bigger bigger bigger bigger file name which doesn't fit .pdf"),
|
aTimelineItemFileContent("An even bigger bigger bigger bigger bigger bigger bigger file name which doesn't fit.pdf"),
|
||||||
|
aTimelineItemFileContent(caption = "A caption"),
|
||||||
|
aTimelineItemFileContent(caption = "An even bigger bigger bigger bigger bigger bigger bigger caption"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aTimelineItemFileContent(
|
fun aTimelineItemFileContent(
|
||||||
fileName: String = "A file.pdf",
|
fileName: String = "A file.pdf",
|
||||||
|
caption: String? = null,
|
||||||
) = TimelineItemFileContent(
|
) = TimelineItemFileContent(
|
||||||
filename = fileName,
|
filename = fileName,
|
||||||
caption = null,
|
caption = caption,
|
||||||
formattedCaption = null,
|
formattedCaption = null,
|
||||||
isEdited = false,
|
isEdited = false,
|
||||||
thumbnailSource = null,
|
thumbnailSource = null,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue