Add some Preview

This commit is contained in:
Benoit Marty 2023-02-10 16:05:11 +01:00 committed by Benoit Marty
parent 23c4b9041a
commit 395d432220
19 changed files with 248 additions and 29 deletions

View file

@ -18,7 +18,9 @@ package io.element.android.x.root
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import io.element.android.features.rageshake.crash.ui.CrashDetectionState import io.element.android.features.rageshake.crash.ui.CrashDetectionState
import io.element.android.features.rageshake.crash.ui.aCrashDetectionState
import io.element.android.features.rageshake.detection.RageshakeDetectionState import io.element.android.features.rageshake.detection.RageshakeDetectionState
import io.element.android.features.rageshake.detection.aRageshakeDetectionState
@Stable @Stable
data class RootState( data class RootState(
@ -27,3 +29,10 @@ data class RootState(
val crashDetectionState: CrashDetectionState, val crashDetectionState: CrashDetectionState,
val eventSink: (RootEvents) -> Unit val eventSink: (RootEvents) -> Unit
) )
fun aRootState() = RootState(
isShowkaseButtonVisible = false,
rageshakeDetectionState = aRageshakeDetectionState(),
crashDetectionState = aCrashDetectionState(),
eventSink = {}
)

View file

@ -24,10 +24,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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 io.element.android.features.rageshake.crash.ui.CrashDetectionEvents import io.element.android.features.rageshake.crash.ui.CrashDetectionEvents
import io.element.android.features.rageshake.crash.ui.CrashDetectionView import io.element.android.features.rageshake.crash.ui.CrashDetectionView
import io.element.android.features.rageshake.crash.ui.aCrashDetectionState
import io.element.android.features.rageshake.detection.RageshakeDetectionEvents import io.element.android.features.rageshake.detection.RageshakeDetectionEvents
import io.element.android.features.rageshake.detection.RageshakeDetectionView import io.element.android.features.rageshake.detection.RageshakeDetectionView
import io.element.android.features.rageshake.detection.aRageshakeDetectionState
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.BooleanPreviewParameter
import io.element.android.tests.uitests.openShowkase import io.element.android.tests.uitests.openShowkase
import io.element.android.x.component.ShowkaseButton import io.element.android.x.component.ShowkaseButton
@ -68,3 +76,24 @@ fun RootView(
) )
} }
} }
@Preview
@Composable
fun RootLightPreview(@PreviewParameter(BooleanPreviewParameter::class) dialogType: Boolean) = ElementPreviewLight { ContentToPreview(dialogType) }
@Preview
@Composable
fun RootDarkPreview(@PreviewParameter(BooleanPreviewParameter::class) dialogType: Boolean) = ElementPreviewDark { ContentToPreview(dialogType) }
@Composable
private fun ContentToPreview(dialogType: Boolean) {
RootView(
aRootState().copy(
isShowkaseButtonVisible = true,
rageshakeDetectionState = aRageshakeDetectionState().copy(showDialog = dialogType),
crashDetectionState = aCrashDetectionState().copy(crashDetected = !dialogType),
)
) {
Text("Children")
}
}

View file

@ -49,22 +49,33 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
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 androidx.compose.ui.unit.sp
import io.element.android.features.messages.actionlist.ActionListEvents import io.element.android.features.messages.actionlist.ActionListEvents
import io.element.android.features.messages.actionlist.ActionListView import io.element.android.features.messages.actionlist.ActionListView
import io.element.android.features.messages.actionlist.anActionListState
import io.element.android.features.messages.actionlist.model.TimelineItemAction import io.element.android.features.messages.actionlist.model.TimelineItemAction
import io.element.android.features.messages.textcomposer.MessageComposerView import io.element.android.features.messages.textcomposer.MessageComposerView
import io.element.android.features.messages.textcomposer.aMessageComposerState
import io.element.android.features.messages.timeline.TimelineView import io.element.android.features.messages.timeline.TimelineView
import io.element.android.features.messages.timeline.aTimelineState
import io.element.android.features.messages.timeline.createTimelineItemContent
import io.element.android.features.messages.timeline.createTimelineItems
import io.element.android.features.messages.timeline.model.TimelineItem import io.element.android.features.messages.timeline.model.TimelineItem
import io.element.android.libraries.core.data.StableCharSequence
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
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.IconButton import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.LogCompositions import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.textcomposer.MessageComposerMode
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -72,7 +83,7 @@ import timber.log.Timber
fun MessagesView( fun MessagesView(
state: MessagesState, state: MessagesState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit, onBackPressed: () -> Unit = {},
) { ) {
LogCompositions(tag = "MessagesScreen", msg = "Root") LogCompositions(tag = "MessagesScreen", msg = "Root")
val itemActionsBottomSheetState = rememberModalBottomSheetState( val itemActionsBottomSheetState = rememberModalBottomSheetState(
@ -197,6 +208,35 @@ fun MessagesViewTopBar(
) )
} }
} }
)
}
@Preview
@Composable
fun MessagesViewLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
fun MessagesViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
MessagesView(
MessagesState(
roomId = RoomId(""),
roomName = "Room name",
roomAvatar = AvatarData("Room name"),
composerState = aMessageComposerState().copy(
text = StableCharSequence("Hello"),
isFullScreen = false,
mode = MessageComposerMode.Normal("Hello"),
),
timelineState = aTimelineState().copy(
timelineItems = createTimelineItems(createTimelineItemContent()),
hasMoreToLoad = false,
),
actionListState = anActionListState(),
eventSink = {}
)
) )
} }

View file

@ -36,3 +36,8 @@ data class ActionListState(
) : Target ) : Target
} }
} }
fun anActionListState() = ActionListState(
target = ActionListState.Target.None,
eventSink = {}
)

View file

@ -28,7 +28,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ListItem import androidx.compose.material.ListItem
import androidx.compose.material.LocalContentColor
import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Text import androidx.compose.material.Text
@ -38,12 +37,16 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.features.messages.actionlist.model.TimelineItemAction import io.element.android.features.messages.actionlist.model.TimelineItemAction
import io.element.android.features.messages.timeline.createMessageEvent
import io.element.android.features.messages.timeline.model.TimelineItem import io.element.android.features.messages.timeline.model.TimelineItem
import io.element.android.libraries.designsystem.components.VectorIcon import io.element.android.libraries.designsystem.components.VectorIcon
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -115,13 +118,13 @@ private fun SheetContent(
text = { text = {
Text( Text(
text = action.title, text = action.title,
color = if (action.destructive) MaterialTheme.colorScheme.error else Color.Unspecified, color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
) )
}, },
icon = { icon = {
VectorIcon( VectorIcon(
resourceId = action.icon, resourceId = action.icon,
tint = if (action.destructive) MaterialTheme.colorScheme.error else LocalContentColor.current, tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
) )
} }
) )
@ -130,3 +133,57 @@ private fun SheetContent(
} }
} }
} }
@Preview
@Composable
fun SheetContentNoneLightPreview() = ElementPreviewLight { ContentNoneToPreview() }
@Preview
@Composable
fun SheetContentNoneDarkPreview() = ElementPreviewDark { ContentNoneToPreview() }
@Composable
private fun ContentNoneToPreview() {
SheetContent(anActionListState())
}
@Preview
@Composable
fun SheetContentLoadingLightPreview() = ElementPreviewLight { ContentLoadingToPreview() }
@Preview
@Composable
fun SheetContentLoadingDarkPreview() = ElementPreviewDark { ContentLoadingToPreview() }
@Composable
private fun ContentLoadingToPreview() {
SheetContent(
anActionListState().copy(target = ActionListState.Target.Loading(createMessageEvent()))
)
}
@Preview
@Composable
fun SheetContentSuccessLightPreview() = ElementPreviewLight { ContentSuccessToPreview() }
@Preview
@Composable
fun SheetContentSuccessDarkPreview() = ElementPreviewDark { ContentSuccessToPreview() }
@Composable
private fun ContentSuccessToPreview() {
SheetContent(
anActionListState().copy(
target = ActionListState.Target.Success(
messageEvent = createMessageEvent(),
actions = persistentListOf(
TimelineItemAction.Reply,
TimelineItemAction.Forward,
TimelineItemAction.Copy,
TimelineItemAction.Edit,
TimelineItemAction.Redact,
)
)
)
)
}

View file

@ -29,3 +29,10 @@ data class MessageComposerState(
) { ) {
val isSendButtonVisible: Boolean = text?.charSequence.isNullOrEmpty().not() val isSendButtonVisible: Boolean = text?.charSequence.isNullOrEmpty().not()
} }
fun aMessageComposerState() = MessageComposerState(
text = StableCharSequence(""),
isFullScreen = false,
mode = MessageComposerMode.Normal(content = ""),
eventSink = {}
)

View file

@ -20,6 +20,7 @@ import androidx.compose.runtime.Immutable
import io.element.android.features.messages.timeline.model.TimelineItem import io.element.android.features.messages.timeline.model.TimelineItem
import io.element.android.libraries.matrix.core.EventId import io.element.android.libraries.matrix.core.EventId
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Immutable @Immutable
data class TimelineState( data class TimelineState(
@ -28,3 +29,10 @@ data class TimelineState(
val highlightedEventId: EventId?, val highlightedEventId: EventId?,
val eventSink: (TimelineEvents) -> Unit val eventSink: (TimelineEvents) -> Unit
) )
fun aTimelineState() = TimelineState(
timelineItems = persistentListOf(),
hasMoreToLoad = false,
highlightedEventId = null,
eventSink = {}
)

View file

@ -64,7 +64,6 @@ import io.element.android.features.messages.timeline.components.TimelineItemUnkn
import io.element.android.features.messages.timeline.model.AggregatedReaction import io.element.android.features.messages.timeline.model.AggregatedReaction
import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition
import io.element.android.features.messages.timeline.model.TimelineItem import io.element.android.features.messages.timeline.model.TimelineItem
import io.element.android.features.messages.timeline.model.TimelineItemGroupPositionProvider
import io.element.android.features.messages.timeline.model.TimelineItemReactions import io.element.android.features.messages.timeline.model.TimelineItemReactions
import io.element.android.features.messages.timeline.model.content.MessagesTimelineItemContentProvider import io.element.android.features.messages.timeline.model.content.MessagesTimelineItemContentProvider
import io.element.android.features.messages.timeline.model.content.TimelineItemContent import io.element.android.features.messages.timeline.model.content.TimelineItemContent
@ -72,6 +71,7 @@ import io.element.android.features.messages.timeline.model.content.TimelineItemE
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.TimelineItemRedactedContent import io.element.android.features.messages.timeline.model.content.TimelineItemRedactedContent
import io.element.android.features.messages.timeline.model.content.TimelineItemTextBasedContent import io.element.android.features.messages.timeline.model.content.TimelineItemTextBasedContent
import io.element.android.features.messages.timeline.model.content.TimelineItemTextContent
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.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -81,7 +81,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
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 import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.PairCombinedPreviewParameter
import io.element.android.libraries.matrix.core.EventId import io.element.android.libraries.matrix.core.EventId
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -366,7 +365,17 @@ fun LoginRootScreenDarkPreview(
@Composable @Composable
private fun ContentToPreview(content: TimelineItemContent) { private fun ContentToPreview(content: TimelineItemContent) {
val timelineItems = persistentListOf( val timelineItems = createTimelineItems(content)
TimelineView(
state = aTimelineState().copy(
timelineItems = timelineItems,
hasMoreToLoad = true,
)
)
}
internal fun createTimelineItems(content: TimelineItemContent): ImmutableList<TimelineItem> {
return persistentListOf(
// 3 items (First Middle Last) with isMine = false // 3 items (First Middle Last) with isMine = false
createMessageEvent( createMessageEvent(
isMine = false, isMine = false,
@ -400,21 +409,13 @@ private fun ContentToPreview(content: TimelineItemContent) {
groupPosition = MessagesItemGroupPosition.First groupPosition = MessagesItemGroupPosition.First
), ),
) )
TimelineView(
state = TimelineState(
timelineItems = timelineItems,
hasMoreToLoad = true,
highlightedEventId = null,
eventSink = {}
)
)
} }
private fun createMessageEvent( internal fun createMessageEvent(
isMine: Boolean, isMine: Boolean = false,
content: TimelineItemContent, content: TimelineItemContent = createTimelineItemContent(),
groupPosition: MessagesItemGroupPosition groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.First
): TimelineItem { ): TimelineItem.MessageEvent {
return TimelineItem.MessageEvent( return TimelineItem.MessageEvent(
id = EventId(Math.random().toString()), id = EventId(Math.random().toString()),
senderId = "senderId", senderId = "senderId",
@ -430,3 +431,10 @@ private fun createMessageEvent(
groupPosition = groupPosition, groupPosition = groupPosition,
) )
} }
internal fun createTimelineItemContent(): TimelineItemContent {
return TimelineItemTextContent(
body = "Text",
htmlDocument = null
)
}

View file

@ -38,6 +38,7 @@ import io.element.android.features.roomlist.components.RoomSummaryRow
import io.element.android.features.roomlist.model.RoomListEvents import io.element.android.features.roomlist.model.RoomListEvents
import io.element.android.features.roomlist.model.RoomListRoomSummary import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.model.RoomListState import io.element.android.features.roomlist.model.RoomListState
import io.element.android.features.roomlist.model.aRoomListState
import io.element.android.features.roomlist.model.stubbedRoomSummaries import io.element.android.features.roomlist.model.stubbedRoomSummaries
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -165,12 +166,11 @@ fun RoomListViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable @Composable
private fun ContentToPreview() { private fun ContentToPreview() {
RoomListContent( RoomListView(
roomSummaries = stubbedRoomSummaries(), aRoomListState().copy(
matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")), matrixUser = MatrixUser(id = UserId("@id"), username = "User#1", avatarData = AvatarData("U")),
onRoomClicked = {}, roomList = stubbedRoomSummaries(),
filter = "filter", filter = "filter",
onFilterChanged = {}, )
onScrollOver = {}
) )
} }

View file

@ -19,6 +19,7 @@ package io.element.android.features.roomlist.model
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.ui.model.MatrixUser import io.element.android.libraries.matrix.ui.model.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Immutable @Immutable
data class RoomListState( data class RoomListState(
@ -27,3 +28,10 @@ data class RoomListState(
val filter: String, val filter: String,
val eventSink: (RoomListEvents) -> Unit val eventSink: (RoomListEvents) -> Unit
) )
fun aRoomListState() = RoomListState(
matrixUser = null,
roomList = persistentListOf(),
filter = "",
eventSink = {}
)

View file

@ -0,0 +1,24 @@
/*
* 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.libraries.designsystem.utils
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class BooleanPreviewParameter : PreviewParameterProvider<Boolean> {
override val values: Sequence<Boolean>
get() = sequenceOf(false, true)
}

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a904cb92b66b66730f5ac1a81b46710668d440e702f5b51320ecf5a29823e47
size 4471

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b
size 4457

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a904cb92b66b66730f5ac1a81b46710668d440e702f5b51320ecf5a29823e47
size 4471

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b
size 4457

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ce15df8f50bf352fa93f5682a23b255ba143c0665d29bc8b7d7ecd0f588011c9
size 14736

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:37edc587d96587aab57013d09def27c807d247d78fa90a69cdcb8d7d9a0344ec
size 13676

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:852dff8969883ccea1811254ea4d68c3f2c30a593ec8411d005b995bcb3ba816
size 41570

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81dc3835d7e7d836f7de9b8f509896ec09f2670a0816fbda3be41e82244efc62
size 39078