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 fbfb1ccd85
commit 35c7d4c132
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.material.icons.filled.Warning
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier 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.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 @Composable
fun TimelineItemEncryptedView( fun TimelineItemEncryptedView(
@ -34,3 +38,22 @@ fun TimelineItemEncryptedView(
modifier = modifier 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.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext 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.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import io.element.android.features.messages.timeline.model.content.TimelineItemImageContent 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 @Composable
fun TimelineItemImageView( fun TimelineItemImageView(
@ -48,7 +54,7 @@ fun TimelineItemImageView(
.aspectRatio(content.aspectRatio), .aspectRatio(content.aspectRatio),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
var isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) } val isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) }
val context = LocalContext.current val context = LocalContext.current
val model = ImageRequest.Builder(context) val model = ImageRequest.Builder(context)
.data(content.imageMeta) .data(content.imageMeta)
@ -57,9 +63,24 @@ fun TimelineItemImageView(
AsyncImage( AsyncImage(
model = model, model = model,
contentDescription = null, contentDescription = null,
placeholder = ColorPainter(MaterialTheme.colorScheme.surfaceVariant), placeholder = debugPlaceholderBackground(ColorPainter(MaterialTheme.colorScheme.surfaceVariant)),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
onSuccess = { isLoading.value = false }, 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 @Preview
@Composable @Composable
internal fun MatrixUserRowLightPreview() = ElementPreviewLight { ContentToPreview() } internal fun TimelineItemInformativeViewLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview @Preview
@Composable @Composable
internal fun MatrixUserRowDarkPreview() = ElementPreviewDark { ContentToPreview() } internal fun TimelineItemInformativeViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable @Composable
private fun ContentToPreview() { private fun ContentToPreview() {

View file

@ -16,24 +16,16 @@
package io.element.android.features.messages.timeline.components 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.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.flowlayout.FlowRow 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.features.messages.timeline.model.TimelineItemReactions
import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import kotlinx.collections.immutable.toPersistentList
@Composable @Composable
fun TimelineItemReactionsView( fun TimelineItemReactionsView(
@ -52,21 +44,22 @@ fun TimelineItemReactionsView(
} }
} }
@Preview
@Composable @Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) { internal fun TimelineItemReactionsViewLightPreview() =
Surface( ElementPreviewLight { ContentToPreview() }
modifier = modifier,
color = MaterialTheme.colorScheme.surfaceVariant, @Preview
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background), @Composable
shape = RoundedCornerShape(corner = CornerSize(12.dp)), internal fun TimelineItemReactionsViewDarkPreview() =
) { ElementPreviewDark { ContentToPreview() }
Row(
modifier = Modifier.padding(vertical = 5.dp, horizontal = 8.dp), @Composable
verticalAlignment = Alignment.CenterVertically private fun ContentToPreview() {
) { TimelineItemReactionsView(
Text(text = reaction.key, fontSize = 12.sp) reactionsState = TimelineItemReactions(
Spacer(modifier = Modifier.width(4.dp)) // Use values from AggregatedReactionProvider
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp) 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.material.icons.filled.Delete
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier 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.features.messages.timeline.model.content.TimelineItemRedactedContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@Composable @Composable
fun TimelineItemRedactedView( fun TimelineItemRedactedView(
@ -34,3 +37,18 @@ fun TimelineItemRedactedView(
modifier = modifier 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.material.icons.filled.Info
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier 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.features.messages.timeline.model.content.TimelineItemUnknownContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@Composable @Composable
fun TimelineItemUnknownView( fun TimelineItemUnknownView(
@ -34,3 +37,18 @@ fun TimelineItemUnknownView(
modifier = modifier 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.annotation.DrawableRes
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import io.element.android.libraries.designsystem.R 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. * but it does not render in preview. In the meantime, you can use this trick to have image.
*/ */
@Composable @Composable
fun debugPlaceholder(@DrawableRes debugPreview: Int) = if (LocalInspectionMode.current) { fun debugPlaceholder(
@DrawableRes debugPreview: Int,
nonDebugPainter: Painter? = null,
) = if (LocalInspectionMode.current) {
painterResource(id = debugPreview) painterResource(id = debugPreview)
} else { } else {
null nonDebugPainter
} }
@Composable @Composable
fun debugPlaceholderBackground() = debugPlaceholder(debugPreview = R.drawable.sample_background) fun debugPlaceholderBackground(nonDebugPainter: Painter? = null): Painter? {
return debugPlaceholder(debugPreview = R.drawable.sample_background, nonDebugPainter)
}
@Composable @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 @Preview
@Composable @Composable
fun MatrixUserRowLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = internal fun MatrixUserRowLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewLight { ContentToPreview(matrixUser) } ElementPreviewLight { ContentToPreview(matrixUser) }
@Preview @Preview
@Composable @Composable
fun MatrixUserRowDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = internal fun MatrixUserRowDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) =
ElementPreviewDark { ContentToPreview(matrixUser) } ElementPreviewDark { ContentToPreview(matrixUser) }
@Composable @Composable