Add Preview for timeline items.

This commit is contained in:
Benoit Marty 2023-02-14 11:34:27 +01:00 committed by Benoit Marty
parent 3cde7d5356
commit 53c8d3ff7c
11 changed files with 265 additions and 39 deletions

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.timeline.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.timeline.model.AggregatedReaction
import io.element.android.features.messages.timeline.model.AggregatedReactionProvider
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.surfaceVariant,
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(corner = CornerSize(12.dp)),
) {
Row(
modifier = Modifier.padding(vertical = 5.dp, horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
// TODO `reaction.isHighlighted` is not used.
Text(text = reaction.key, fontSize = 12.sp)
Spacer(modifier = Modifier.width(4.dp))
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp)
}
}
}
@Preview
@Composable
internal fun MessagesReactionButtonLightPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) =
ElementPreviewLight { ContentToPreview(reaction) }
@Preview
@Composable
internal fun MessagesReactionButtonDarkPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) =
ElementPreviewDark { ContentToPreview(reaction) }
@Composable
private fun ContentToPreview(reaction: AggregatedReaction) {
MessagesReactionButton(reaction)
}

View file

@ -20,7 +20,11 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.features.messages.timeline.model.content.TimelineItemEncryptedContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import org.matrix.rustcomponents.sdk.EncryptedMessage
@Composable
fun TimelineItemEncryptedView(
@ -34,3 +38,22 @@ fun TimelineItemEncryptedView(
modifier = modifier
)
}
@Preview
@Composable
internal fun TimelineItemEncryptedViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemEncryptedViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemEncryptedView(
content = TimelineItemEncryptedContent(
encryptedMessage = EncryptedMessage.Unknown,
)
)
}

View file

@ -28,9 +28,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import coil.compose.AsyncImage
import coil.request.ImageRequest
import io.element.android.features.messages.timeline.model.content.TimelineItemImageContent
import io.element.android.features.messages.timeline.model.content.TimelineItemImageContentProvider
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
@Composable
fun TimelineItemImageView(
@ -48,7 +54,7 @@ fun TimelineItemImageView(
.aspectRatio(content.aspectRatio),
contentAlignment = Alignment.Center,
) {
var isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) }
val isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) }
val context = LocalContext.current
val model = ImageRequest.Builder(context)
.data(content.imageMeta)
@ -57,9 +63,24 @@ fun TimelineItemImageView(
AsyncImage(
model = model,
contentDescription = null,
placeholder = ColorPainter(MaterialTheme.colorScheme.surfaceVariant),
placeholder = debugPlaceholderBackground(ColorPainter(MaterialTheme.colorScheme.surfaceVariant)),
contentScale = ContentScale.Crop,
onSuccess = { isLoading.value = false },
)
}
}
@Preview
@Composable
internal fun TimelineItemImageViewLightPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) =
ElementPreviewLight { ContentToPreview(content) }
@Preview
@Composable
internal fun TimelineItemImageViewDarkPreview(@PreviewParameter(TimelineItemImageContentProvider::class) content: TimelineItemImageContent) =
ElementPreviewDark { ContentToPreview(content) }
@Composable
private fun ContentToPreview(content: TimelineItemImageContent) {
TimelineItemImageView(content)
}

View file

@ -65,11 +65,11 @@ fun TimelineItemInformativeView(
@Preview
@Composable
internal fun MatrixUserRowLightPreview() = ElementPreviewLight { ContentToPreview() }
internal fun TimelineItemInformativeViewLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun MatrixUserRowDarkPreview() = ElementPreviewDark { ContentToPreview() }
internal fun TimelineItemInformativeViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {

View file

@ -16,24 +16,16 @@
package io.element.android.features.messages.timeline.components
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.flowlayout.FlowRow
import io.element.android.features.messages.timeline.model.AggregatedReaction
import io.element.android.features.messages.timeline.model.AggregatedReactionProvider
import io.element.android.features.messages.timeline.model.TimelineItemReactions
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import kotlinx.collections.immutable.toPersistentList
@Composable
fun TimelineItemReactionsView(
@ -52,21 +44,22 @@ fun TimelineItemReactionsView(
}
}
@Preview
@Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.surfaceVariant,
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(corner = CornerSize(12.dp)),
) {
Row(
modifier = Modifier.padding(vertical = 5.dp, horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = reaction.key, fontSize = 12.sp)
Spacer(modifier = Modifier.width(4.dp))
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp)
}
}
internal fun TimelineItemReactionsViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemReactionsViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemReactionsView(
reactionsState = TimelineItemReactions(
// Use values from AggregatedReactionProvider
reactions = AggregatedReactionProvider().values.toPersistentList()
)
)
}

View file

@ -20,7 +20,10 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.features.messages.timeline.model.content.TimelineItemRedactedContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@Composable
fun TimelineItemRedactedView(
@ -34,3 +37,18 @@ fun TimelineItemRedactedView(
modifier = modifier
)
}
@Preview
@Composable
internal fun TimelineItemRedactedViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemRedactedViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemRedactedView(TimelineItemRedactedContent)
}

View file

@ -20,7 +20,10 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.features.messages.timeline.model.content.TimelineItemUnknownContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@Composable
fun TimelineItemUnknownView(
@ -34,3 +37,18 @@ fun TimelineItemUnknownView(
modifier = modifier
)
}
@Preview
@Composable
internal fun TimelineItemUnknownViewLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemUnknownViewDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemUnknownView(TimelineItemUnknownContent)
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.timeline.model
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class AggregatedReactionProvider : PreviewParameterProvider<AggregatedReaction> {
override val values: Sequence<AggregatedReaction>
get() = sequenceOf(
aAggregatedReaction(),
aAggregatedReaction().copy(count = "88"),
aAggregatedReaction().copy(isHighlighted = true),
aAggregatedReaction().copy(count = "88", isHighlighted = true),
)
}
fun aAggregatedReaction() = AggregatedReaction(
key = "👍",
count = "1", // TODO Why is it a String?
isHighlighted = false,
)

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.timeline.model.content
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.media.MediaResolver
open class TimelineItemImageContentProvider : PreviewParameterProvider<TimelineItemImageContent> {
override val values: Sequence<TimelineItemImageContent>
get() = sequenceOf(
aTimelineItemImageContent(),
aTimelineItemImageContent().copy(aspectRatio = 1.0f),
aTimelineItemImageContent().copy(aspectRatio = 1.5f),
)
}
fun aTimelineItemImageContent() = TimelineItemImageContent(
body = "a body",
imageMeta = MediaResolver.Meta(source = null, kind = MediaResolver.Kind.Content),
blurhash = null,
aspectRatio = 0.5f,
)

View file

@ -18,6 +18,7 @@ package io.element.android.libraries.designsystem.preview
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import io.element.android.libraries.designsystem.R
@ -27,14 +28,21 @@ import io.element.android.libraries.designsystem.R
* but it does not render in preview. In the meantime, you can use this trick to have image.
*/
@Composable
fun debugPlaceholder(@DrawableRes debugPreview: Int) = if (LocalInspectionMode.current) {
fun debugPlaceholder(
@DrawableRes debugPreview: Int,
nonDebugPainter: Painter? = null,
) = if (LocalInspectionMode.current) {
painterResource(id = debugPreview)
} else {
null
nonDebugPainter
}
@Composable
fun debugPlaceholderBackground() = debugPlaceholder(debugPreview = R.drawable.sample_background)
fun debugPlaceholderBackground(nonDebugPainter: Painter? = null): Painter? {
return debugPlaceholder(debugPreview = R.drawable.sample_background, nonDebugPainter)
}
@Composable
fun debugPlaceholderAvatar() = debugPlaceholder(debugPreview = R.drawable.sample_avatar)
fun debugPlaceholderAvatar(nonDebugPainter: Painter? = null): Painter? {
return debugPlaceholder(debugPreview = R.drawable.sample_avatar, nonDebugPainter)
}

View file

@ -89,12 +89,12 @@ fun MatrixUserRow(
@Preview
@Composable
fun MatrixUserRowLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
internal fun MatrixUserRowLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewLight { ContentToPreview(matrixUser) }
@Preview
@Composable
fun MatrixUserRowDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
internal fun MatrixUserRowDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewDark { ContentToPreview(matrixUser) }
@Composable