Merge pull request #724 from vector-im/feature/bma/designIteration

Design iteration
This commit is contained in:
Benoit Marty 2023-06-29 18:57:44 +02:00 committed by GitHub
commit 00d8161bdc
363 changed files with 1502 additions and 977 deletions

View file

@ -4,7 +4,9 @@
<w>backstack</w>
<w>homeserver</w>
<w>kover</w>
<w>measurables</w>
<w>onboarding</w>
<w>placeables</w>
<w>showkase</w>
<w>textfields</w>
</words>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@mipmap/ic_launcher_round"
android:insetTop="80dp"
android:insetRight="80dp"
android:insetBottom="80dp"
android:insetLeft="80dp" />

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#00000000" />
</shape>

View file

@ -17,8 +17,8 @@
<resources>
<style name="Theme.ElementX.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/black</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<item name="windowSplashScreenBackground">@color/splashscreen_bg_dark</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/transparent</item>
<item name="postSplashScreenTheme">@style/Theme.ElementX</item>
</style>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@ -16,11 +15,8 @@
-->
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<!-- Must be equal to DarkDesignTokens.colorThemeBg -->
<color name="splashscreen_bg_dark">#FF101317</color>
<!-- Must be equal to LightDesignTokens.colorThemeBg -->
<color name="splashscreen_bg_light">#FFFFFFFF</color>
</resources>

View file

@ -16,9 +16,9 @@
-->
<resources>
<style name="Theme.ElementX.Splash" parent="Theme.SplashScreen.IconBackground">
<item name="windowSplashScreenBackground">@color/white</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<style name="Theme.ElementX.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashscreen_bg_light</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/transparent</item>
<item name="postSplashScreenTheme">@style/Theme.ElementX</item>
</style>
<style name="Theme.ElementX" parent="Theme.Material3.Light" />

View file

@ -32,7 +32,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
@ -50,14 +49,12 @@ import io.element.android.features.invitelist.impl.model.InviteSender
import io.element.android.libraries.designsystem.ElementTextStyles
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.noFontPadding
import io.element.android.libraries.designsystem.theme.roomListUnreadIndicator
import io.element.android.libraries.ui.strings.CommonStrings
private val minHeight = 72.dp
@ -161,8 +158,7 @@ internal fun DefaultInviteSummaryRow(
}
}
val unreadIndicatorColor = if (invite.isNew) MaterialTheme.roomListUnreadIndicator() else Color.Transparent
UnreadIndicatorAtom(color = unreadIndicatorColor)
UnreadIndicatorAtom(isVisible = invite.isNew)
}
}

View file

@ -39,7 +39,6 @@ import androidx.compose.material.icons.outlined.AddReaction
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -48,6 +47,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@ -86,8 +86,8 @@ fun ActionListView(
onEmojiReactionClicked: (String, TimelineItem.Event) -> Unit,
onCustomReactionClicked: (TimelineItem.Event) -> Unit,
modifier: Modifier = Modifier,
sheetState: SheetState = rememberModalBottomSheetState()
) {
val sheetState = rememberModalBottomSheetState()
val coroutineScope = rememberCoroutineScope()
val targetItem = (state.target as? ActionListState.Target.Success)?.event
@ -118,22 +118,21 @@ fun ActionListView(
}
fun onDismiss() {
sheetState.hide(coroutineScope) {
state.eventSink(ActionListEvents.Clear)
}
state.eventSink(ActionListEvents.Clear)
}
if (targetItem != null) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = ::onDismiss,
modifier = modifier,
) {
SheetContent(
state = state,
onActionClicked = ::onItemActionClicked,
onEmojiReactionClicked = ::onEmojiReactionClicked,
onCustomReactionClicked = ::onCustomReactionClicked,
modifier = modifier
modifier = Modifier
.padding(bottom = 32.dp)
// .navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044
// .imePadding()
@ -191,7 +190,7 @@ private fun SheetContent(
},
text = {
Text(
text = action.title,
text = stringResource(id = action.titleRes),
color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
)
},

View file

@ -17,20 +17,22 @@
package io.element.android.features.messages.impl.actionlist.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import io.element.android.libraries.designsystem.VectorIcons
import io.element.android.libraries.ui.strings.CommonStrings
@Immutable
sealed class TimelineItemAction(
val title: String,
@StringRes val titleRes: Int,
@DrawableRes val icon: Int,
val destructive: Boolean = false
) {
object Forward : TimelineItemAction("Forward", VectorIcons.Forward)
object Copy : TimelineItemAction("Copy", VectorIcons.Copy)
object Redact : TimelineItemAction("Redact", VectorIcons.Delete, destructive = true)
object Reply : TimelineItemAction("Reply", VectorIcons.Reply)
object Edit : TimelineItemAction("Edit", VectorIcons.Edit)
object Developer : TimelineItemAction("Developer", VectorIcons.DeveloperMode)
object ReportContent : TimelineItemAction("Report content", VectorIcons.ReportContent, destructive = true)
object Forward : TimelineItemAction(CommonStrings.action_forward, VectorIcons.Forward)
object Copy : TimelineItemAction(CommonStrings.action_copy, VectorIcons.Copy)
object Redact : TimelineItemAction(CommonStrings.action_remove, VectorIcons.Delete, destructive = true)
object Reply : TimelineItemAction(CommonStrings.action_reply, VectorIcons.Reply)
object Edit : TimelineItemAction(CommonStrings.action_edit, VectorIcons.Edit)
object Developer : TimelineItemAction(CommonStrings.action_view_source, VectorIcons.DeveloperMode)
object ReportContent : TimelineItemAction(CommonStrings.action_report_content, VectorIcons.ReportContent, destructive = true)
}

View file

@ -44,6 +44,8 @@ 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
import io.element.android.libraries.designsystem.theme.messageFromMeBackground
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
import io.element.android.libraries.theme.ElementTheme
private val BUBBLE_RADIUS = 12.dp
@ -97,14 +99,11 @@ fun MessageEventBubble(
}
}
val backgroundBubbleColor = if (state.isHighlighted) {
ElementTheme.legacyColors.messageHighlightedBackground
// Ignore state.isHighlighted for now, we need a design decision on it.
val backgroundBubbleColor = if (state.isMine) {
ElementTheme.colors.messageFromMeBackground
} else {
if (state.isMine) {
ElementTheme.legacyColors.messageFromMeBackground
} else {
ElementTheme.legacyColors.messageFromOtherBackground
}
ElementTheme.colors.messageFromOtherBackground
}
val bubbleShape = bubbleShape()
Box(

View file

@ -41,18 +41,15 @@ private val CORNER_RADIUS = 8.dp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MessageStateEventContainer(
isHighlighted: Boolean,
@Suppress("UNUSED_PARAMETER") isHighlighted: Boolean,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
content: @Composable () -> Unit = {},
) {
val backgroundColor = if (isHighlighted) {
ElementTheme.legacyColors.messageHighlightedBackground
} else {
Color.Companion.Transparent
}
// Ignore isHighlighted for now, we need a design decision on it.
val backgroundColor = Color.Transparent
val shape = RoundedCornerShape(CORNER_RADIUS)
Surface(
modifier = modifier

View file

@ -58,15 +58,19 @@ fun TimelineEventTimestampView(
val hasMessageSendingFailed = event.sendState is EventSendState.SendingFailed
val isMessageEdited = (event.content as? TimelineItemTextBasedContent)?.isEdited.orFalse()
val tint = if (hasMessageSendingFailed) MaterialTheme.colorScheme.error else null
val clickModifier = if (hasMessageSendingFailed) {
Modifier.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
indication = rememberRipple(bounded = false),
interactionSource = MutableInteractionSource()
)
} else {
Modifier
}
Row(
modifier = Modifier
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
enabled = true,
indication = rememberRipple(bounded = false),
interactionSource = MutableInteractionSource()
)
.then(clickModifier)
.padding(start = 16.dp) // Add extra padding for touch target size
.then(modifier),
verticalAlignment = Alignment.CenterVertically,

View file

@ -41,7 +41,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@ -59,7 +58,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.designsystem.ElementTextStyles
import io.element.android.libraries.designsystem.components.EqualWidthColumn
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -71,6 +69,8 @@ import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
@ -268,7 +268,7 @@ private fun MessageEventBubbleContent(
}
} else {
Box(modifier) {
ContentView(modifier = contentModifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp))
ContentView(modifier = contentModifier)
TimelineEventTimestampView(
event = event,
onClick = onTimestampClicked,
@ -316,7 +316,11 @@ private fun MessageEventBubbleContent(
val contentModifier = if (isMediaItem) {
Modifier.clip(RoundedCornerShape(12.dp))
} else {
Modifier
if (inReplyToDetails != null) {
Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp)
} else {
Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp)
}
}
ContentAndTimestampView(
@ -363,20 +367,19 @@ private fun ReplyToContent(
}
Column(verticalArrangement = Arrangement.SpaceBetween) {
Text(
senderName,
style = ElementTextStyles.Regular.caption2.copy(fontWeight = FontWeight.Medium),
text = senderName,
style = ElementTheme.typography.fontBodySmMedium,
textAlign = TextAlign.Start,
color = MaterialTheme.colorScheme.primary,
color = ElementTheme.materialColors.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = text.orEmpty(),
style = ElementTextStyles.Regular.caption1,
style = ElementTheme.typography.fontBodyMdRegular,
textAlign = TextAlign.Start,
color = ElementTheme.legacyColors.placeholder,
maxLines = 1,
color = ElementTheme.materialColors.secondary,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
@ -455,6 +458,76 @@ private fun ContentToPreview() {
}
}
@Preview
@Composable
internal fun TimelineItemEventRowWithReplyLightPreview() =
ElementPreviewLight { ContentToPreviewWithReply() }
@Preview
@Composable
internal fun TimelineItemEventRowWithReplyDarkPreview() =
ElementPreviewDark { ContentToPreviewWithReply() }
@Composable
private fun ContentToPreviewWithReply() {
Column {
sequenceOf(false, true).forEach {
val replyContent = if(it) {
// Short
"Message which are being replied."
} else {
// Long, to test 2 lines and ellipsis)
"Message which are being replied, and which was long enough to be displayed on two lines (only!)."
}
TimelineItemEventRow(
event = aTimelineItemEvent(
isMine = it,
content = aTimelineItemTextContent().copy(
body = "A long text which will be displayed on several lines and" +
" hopefully can be manually adjusted to test different behaviors."
),
inReplyTo = aInReplyToReady(replyContent)
),
isHighlighted = false,
onClick = {},
onLongClick = {},
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
onTimestampClicked = {},
)
TimelineItemEventRow(
event = aTimelineItemEvent(
isMine = it,
content = aTimelineItemImageContent().copy(
aspectRatio = 5f
),
inReplyTo = aInReplyToReady(replyContent)
),
isHighlighted = false,
onClick = {},
onLongClick = {},
onUserDataClick = {},
inReplyToClick = {},
onReactionClick = { _, _ -> },
onTimestampClicked = {},
)
}
}
}
private fun aInReplyToReady(
replyContent: String
): InReplyTo.Ready {
return InReplyTo.Ready(
eventId = EventId("\$event"),
content = MessageContent(replyContent, null, false, TextMessageType(replyContent, null)),
senderId = UserId("@Sender:domain"),
senderDisplayName = "Sender",
senderAvatarUrl = null,
)
}
@Preview
@Composable
internal fun TimelineItemEventRowTimestampLightPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) =

View file

@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
@ -50,6 +51,7 @@ fun TimelineItemStateEventRow(
Box(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.wrapContentHeight(),
contentAlignment = Alignment.Center
) {

View file

@ -38,9 +38,7 @@ fun CustomReactionBottomSheet(
val coroutineScope = rememberCoroutineScope()
fun onDismiss() {
sheetState.hide(coroutineScope) {
state.eventSink(CustomReactionEvents.UpdateSelectedEvent(null))
}
state.eventSink(CustomReactionEvents.UpdateSelectedEvent(null))
}
fun onEmojiSelectedDismiss(emoji: Emoji) {

View file

@ -51,3 +51,11 @@ fun TimelineItem.Event.toExtraPadding(): ExtraPadding {
// A space and a few unbreakable spaces
return ExtraPadding(" " + "\u00A0".repeat(strLen))
}
fun ExtraPadding.strBigger(): String {
return if (str.isEmpty()) {
str
} else {
str + "\u00A0\u00A0\u00A0"
}
}

View file

@ -59,7 +59,8 @@ fun TimelineItemInformativeView(
fontStyle = FontStyle.Italic,
color = MaterialTheme.colorScheme.secondary,
fontSize = 14.sp,
text = text + extraPadding.str
// Since the font size is smaller, add more space to extra padding, to not overlap with the timestamp
text = text + extraPadding.strBigger()
)
}
}

View file

@ -38,7 +38,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text
@ -49,15 +48,12 @@ private val CORNER_RADIUS = 8.dp
fun GroupHeaderView(
text: String,
isExpanded: Boolean,
isHighlighted: Boolean,
@Suppress("UNUSED_PARAMETER") isHighlighted: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val backgroundColor = if (isHighlighted) {
ElementTheme.legacyColors.messageHighlightedBackground
} else {
Color.Companion.Transparent
}
// Ignore isHighlighted for now, we need a design decision on it.
val backgroundColor = Color.Companion.Transparent
val shape = RoundedCornerShape(CORNER_RADIUS)
Box(

View file

@ -25,7 +25,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
@ -40,6 +39,7 @@ 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.features.messages.impl.R
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import kotlinx.coroutines.launch
@Composable

View file

@ -38,8 +38,6 @@ import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.PersonAddAlt
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@ -70,6 +68,9 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.LargeHeightPreview
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
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.Scaffold
@ -192,12 +193,11 @@ internal fun RoomDetailsTopBar(
Icon(Icons.Default.MoreVert, "")
}
DropdownMenu(
modifier = Modifier.widthIn(200.dp),
expanded = showMenu,
onDismissRequest = { showMenu = false },
) {
DropdownMenuItem(
text = { Text(stringResource(id = CommonStrings.action_edit)) },
text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_edit)) },
onClick = {
// Explicitly close the menu before handling the action, as otherwise it stays open during the
// transition and renders really badly.

View file

@ -0,0 +1,91 @@
/*
* 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.roomlist.impl
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
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.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
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.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun InvitesEntryPointView(
onInvitesClicked: () -> Unit,
state: InvitesState,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.clickable(role = Role.Button, onClick = onInvitesClicked)
.padding(start = 24.dp, end = 16.dp)
.align(Alignment.CenterEnd)
.heightIn(min = 40.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(CommonStrings.action_invites_list),
style = ElementTheme.typography.fontBodyMdMedium,
)
if (state == InvitesState.NewInvites) {
Spacer(Modifier.width(8.dp))
UnreadIndicatorAtom()
}
}
}
}
@Preview
@Composable
internal fun InvitesEntryPointViewLightPreview(@PreviewParameter(InvitesStateProvider::class) state: InvitesState) =
ElementPreviewLight { ContentToPreview(state) }
@Preview
@Composable
internal fun InvitesEntryPointViewDarkPreview(@PreviewParameter(InvitesStateProvider::class) state: InvitesState) =
ElementPreviewDark { ContentToPreview(state) }
@Composable
private fun ContentToPreview(state: InvitesState) {
InvitesEntryPointView(
onInvitesClicked = {},
state = state,
)
}

View file

@ -0,0 +1,27 @@
/*
* 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.roomlist.impl
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class InvitesStateProvider : PreviewParameterProvider<InvitesState> {
override val values: Sequence<InvitesState>
get() = sequenceOf(
InvitesState.SeenInvites,
InvitesState.NewInvites,
)
}

View file

@ -17,18 +17,12 @@
package io.element.android.features.roomlist.impl
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
@ -42,17 +36,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.leaveroom.api.LeaveRoomView
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
import io.element.android.features.roomlist.impl.components.RequestVerificationHeader
@ -61,18 +52,15 @@ import io.element.android.features.roomlist.impl.components.RoomListTopBar
import io.element.android.features.roomlist.impl.components.RoomSummaryRow
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.search.RoomListSearchResultView
import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Divider
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.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.designsystem.R as DrawableR
@Composable
@ -206,7 +194,7 @@ fun RoomListContent(
if (state.invitesState != InvitesState.NoInvites) {
item {
InvitesEntryPointView(onInvitesClicked, state)
InvitesEntryPointView(onInvitesClicked, state.invitesState)
}
}
@ -251,38 +239,6 @@ fun RoomListContent(
)
}
@Composable
private fun InvitesEntryPointView(
onInvitesClicked: () -> Unit,
state: RoomListState,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier
.clickable(role = Role.Button, onClick = onInvitesClicked)
.padding(horizontal = 16.dp)
.align(Alignment.CenterEnd)
.heightIn(min = 48.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(CommonStrings.action_invites_list),
fontSize = 14.sp,
style = MaterialTheme.typography.bodyMedium,
)
if (state.invitesState == InvitesState.NewInvites) {
Spacer(Modifier.width(8.dp))
UnreadIndicatorAtom()
}
}
}
}
internal fun RoomListRoomSummary.contentType() = isPlaceholder
@Preview

View file

@ -19,12 +19,10 @@ package io.element.android.features.roomlist.impl.components
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
@ -48,6 +46,9 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
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.MediumTopAppBar
@ -58,6 +59,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@ -137,12 +139,20 @@ private fun DefaultRoomListTopBar(
IconButton(
onClick = onSearchClicked,
) {
Icon(Icons.Default.Search, contentDescription = stringResource(CommonStrings.action_search))
Icon(
imageVector = Icons.Default.Search,
tint = ElementTheme.materialColors.secondary,
contentDescription = stringResource(CommonStrings.action_search),
)
}
IconButton(
onClick = { showMenu = !showMenu }
) {
Icon(Icons.Default.MoreVert, contentDescription = null)
Icon(
imageVector = Icons.Default.MoreVert,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
}
DropdownMenu(
expanded = showMenu,
@ -153,16 +163,28 @@ private fun DefaultRoomListTopBar(
showMenu = false
onMenuActionClicked(RoomListMenuAction.InviteFriends)
},
text = { Text(stringResource(id = CommonStrings.action_invite)) },
leadingIcon = { Icon(Icons.Default.Share, contentDescription = null) }
text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_invite)) },
leadingIcon = {
Icon(
Icons.Outlined.Share,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
}
)
DropdownMenuItem(
onClick = {
showMenu = false
onMenuActionClicked(RoomListMenuAction.ReportBug)
},
text = { Text(stringResource(id = CommonStrings.common_report_a_bug)) },
leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) }
text = { DropdownMenuItemText(stringResource(id = CommonStrings.common_report_a_bug)) },
leadingIcon = {
Icon(
Icons.Outlined.BugReport,
tint = ElementTheme.materialColors.secondary,
contentDescription = null,
)
}
)
}
},

View file

@ -36,6 +36,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.roomListPlaceholder
import io.element.android.libraries.theme.ElementTheme
/**
@ -55,7 +56,7 @@ internal fun RoomSummaryPlaceholderRow(
modifier = Modifier
.size(AvatarSize.RoomListItem.dp)
.align(Alignment.CenterVertically)
.background(color = ElementTheme.colors.textPlaceholder, shape = CircleShape)
.background(color = ElementTheme.colors.roomListPlaceholder, shape = CircleShape)
)
Column(
modifier = Modifier

View file

@ -35,19 +35,16 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.features.roomlist.impl.model.RoomListRoomSummary
import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvider
import io.element.android.libraries.core.extensions.orEmpty
@ -59,7 +56,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.roomListRoomMessage
import io.element.android.libraries.designsystem.theme.roomListRoomMessageDate
import io.element.android.libraries.designsystem.theme.roomListRoomName
import io.element.android.libraries.designsystem.theme.roomListUnreadIndicator
import io.element.android.libraries.designsystem.theme.unreadIndicator
import io.element.android.libraries.theme.ElementTheme
internal val minHeight = 84.dp
@ -136,9 +134,7 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) {
modifier = Modifier
.weight(1f)
.padding(end = 16.dp),
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
style = MaterialTheme.typography.bodyMedium,
style = ElementTheme.typography.fontBodyLgMedium,
text = room.name,
color = MaterialTheme.roomListRoomName(),
maxLines = 1,
@ -146,9 +142,13 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) {
)
// Timestamp
Text(
fontSize = 12.sp,
text = room.timestamp ?: "",
color = MaterialTheme.roomListRoomMessageDate(),
style = ElementTheme.typography.fontBodySmRegular,
color = if (room.hasUnread) {
ElementTheme.colors.unreadIndicator
} else {
MaterialTheme.roomListRoomMessageDate()
},
)
}
@ -163,17 +163,15 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) {
.padding(end = 28.dp),
text = attributedLastMessage,
color = MaterialTheme.roomListRoomMessage(),
fontSize = 14.sp,
style = MaterialTheme.typography.bodySmall,
style = ElementTheme.typography.fontBodyMdRegular,
minLines = 2,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
// Unread
val unreadIndicatorColor =
if (room.hasUnread) MaterialTheme.roomListUnreadIndicator() else Color.Transparent
UnreadIndicatorAtom(
modifier = Modifier.padding(top = 3.dp),
color = unreadIndicatorColor,
isVisible = room.hasUnread,
)
}

View file

@ -138,6 +138,7 @@ internal fun RoomListSearchResultContent(
.fillMaxWidth()
.focusRequester(focusRequester),
value = filter,
singleLine = true,
onValueChange = { state.eventSink(RoomListEvents.UpdateFilter(it)) },
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,

View file

@ -29,6 +29,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.roomListPlaceholder
import io.element.android.libraries.theme.ElementTheme
@Composable
@ -36,7 +37,7 @@ fun PlaceholderAtom(
width: Dp,
height: Dp,
modifier: Modifier = Modifier,
color: Color = ElementTheme.colors.textPlaceholder,
color: Color = ElementTheme.colors.roomListPlaceholder,
) {
Box(
modifier = modifier

View file

@ -20,7 +20,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -30,19 +29,21 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.roomListUnreadIndicator
import io.element.android.libraries.designsystem.theme.unreadIndicator
import io.element.android.libraries.theme.ElementTheme
@Composable
fun UnreadIndicatorAtom(
modifier: Modifier = Modifier,
size: Dp = 12.dp,
color: Color = MaterialTheme.roomListUnreadIndicator(),
color: Color = ElementTheme.colors.unreadIndicator,
isVisible: Boolean = true,
) {
Box(
modifier = modifier
.size(size)
.clip(CircleShape)
.background(color)
.background(if (isVisible) color else Color.Transparent)
)
}

View file

@ -27,10 +27,10 @@ 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 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.theme.ElementTheme
/**
* @param modifier Classical modifier.
@ -87,7 +87,7 @@ private fun ContentToPreview() {
) {
Text(
text = "Content",
fontSize = 40.sp
style = ElementTheme.typography.fontHeadingXlBold
)
}
},
@ -99,7 +99,7 @@ private fun ContentToPreview() {
) {
Text(
text = "Header",
fontSize = 40.sp
style = ElementTheme.typography.fontHeadingXlBold
)
}
},
@ -111,7 +111,7 @@ private fun ContentToPreview() {
) {
Text(
text = "Footer",
fontSize = 40.sp
style = ElementTheme.typography.fontHeadingXlBold
)
}
}

View file

@ -30,11 +30,11 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.designsystem.R
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.theme.ElementTheme
/**
* Page for onboarding screens, with content and optional footer.
@ -106,7 +106,7 @@ private fun ContentToPreview() {
) {
Text(
text = "Content",
fontSize = 40.sp
style = ElementTheme.typography.fontHeadingXlBold
)
}
},
@ -118,7 +118,7 @@ private fun ContentToPreview() {
) {
Text(
text = "Footer",
fontSize = 40.sp
style = ElementTheme.typography.fontHeadingXlBold
)
}
}

View file

@ -26,8 +26,6 @@ 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.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
@ -39,8 +37,7 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.debugPlaceholderAvatar
import io.element.android.libraries.designsystem.text.toSp
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.AvatarGradientEnd
import io.element.android.libraries.theme.AvatarGradientStart
import io.element.android.libraries.theme.ElementTheme
import timber.log.Timber
@Composable
@ -89,16 +86,10 @@ private fun InitialsAvatar(
avatarData: AvatarData,
modifier: Modifier = Modifier,
) {
val initialsGradient = Brush.linearGradient(
listOf(
AvatarGradientStart,
AvatarGradientEnd,
),
start = Offset(0.0f, 100f),
end = Offset(100f, 0f)
)
// Use temporary color for default avatar background
val avatarColor = ElementTheme.colors.bgActionPrimaryDisabled
Box(
modifier.background(brush = initialsGradient),
modifier.background(color = avatarColor),
) {
Text(
modifier = Modifier.align(Alignment.Center),

View file

@ -0,0 +1,82 @@
/*
* 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.ruler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
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.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
/**
* Horizontal ruler is a debug composable that displays a horizontal ruler.
* It can be used to display the horizontal ruler in the composable preview.
*/
@Composable
fun HorizontalRuler(
modifier: Modifier = Modifier,
) {
val baseColor = Color.Magenta
val alphaBaseColor = baseColor.copy(alpha = 0.2f)
Row(modifier = modifier.fillMaxWidth()) {
repeat(50) {
HorizontalRulerItem(1.dp, alphaBaseColor)
HorizontalRulerItem(2.dp, baseColor)
HorizontalRulerItem(1.dp, alphaBaseColor)
HorizontalRulerItem(2.dp, baseColor)
HorizontalRulerItem(5.dp, alphaBaseColor)
HorizontalRulerItem(2.dp, baseColor)
HorizontalRulerItem(1.dp, alphaBaseColor)
HorizontalRulerItem(2.dp, baseColor)
HorizontalRulerItem(1.dp, alphaBaseColor)
HorizontalRulerItem(10.dp, baseColor)
}
}
}
@Composable
private fun HorizontalRulerItem(height: Dp, color: Color) {
Spacer(
modifier = Modifier
.size(height = height, width = 1.dp)
.background(color = color)
)
}
@Preview
@Composable
internal fun HorizontalRulerLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun HorizontalRulerDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
HorizontalRuler()
}

View file

@ -0,0 +1,81 @@
/*
* 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.ruler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
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.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
/**
* Vertical ruler is a debug composable that displays a vertical ruler.
* It can be used to display the vertical ruler in the composable preview.
*/
@Composable
fun VerticalRuler(
modifier: Modifier = Modifier,
) {
val baseColor = Color.Red
val alphaBaseColor = baseColor.copy(alpha = 0.2f)
Column(modifier = modifier.fillMaxHeight()) {
repeat(50) {
VerticalRulerItem(1.dp, alphaBaseColor)
VerticalRulerItem(2.dp, baseColor)
VerticalRulerItem(1.dp, alphaBaseColor)
VerticalRulerItem(2.dp, baseColor)
VerticalRulerItem(5.dp, alphaBaseColor)
VerticalRulerItem(2.dp, baseColor)
VerticalRulerItem(1.dp, alphaBaseColor)
VerticalRulerItem(2.dp, baseColor)
VerticalRulerItem(1.dp, alphaBaseColor)
VerticalRulerItem(10.dp, baseColor)
}
}
}
@Composable
private fun VerticalRulerItem(width: Dp, color: Color) {
Spacer(
modifier = Modifier
.size(height = 1.dp, width = width)
.background(color = color)
)
}
@Preview
@Composable
internal fun VerticalRulerLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun VerticalRulerDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
VerticalRuler()
}

View file

@ -0,0 +1,83 @@
/*
* 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.ruler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
/**
* Debug tool to add a vertical and a horizontal ruler on top of the content.
*/
@Composable
fun WithRulers(
modifier: Modifier = Modifier,
xRulersOffset: Dp = 0.dp,
yRulersOffset: Dp = 0.dp,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = {
content()
VerticalRuler()
HorizontalRuler()
},
measurePolicy = { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
// Use layout size of the first item (the content)
layout(
width = placeables.first().width,
height = placeables.first().height
) {
placeables.forEachIndexed { index, placeable ->
if (index == 0) {
placeable.place(0, 0)
} else {
placeable.place(xRulersOffset.roundToPx(), yRulersOffset.roundToPx())
}
}
}
}
)
}
@Preview
@Composable
internal fun WithRulerLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun WithRulerDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
OutlinedButton(onClick = {}) {
Text(text = "A Button with rulers on it!")
}
}
}

View file

@ -22,6 +22,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.compound.generated.SemanticColors
import io.element.android.libraries.theme.previews.ColorListPreview
import kotlinx.collections.immutable.persistentMapOf
@ -37,8 +39,31 @@ fun MaterialTheme.roomListRoomMessage() = colorScheme.secondary
@Composable
fun MaterialTheme.roomListRoomMessageDate() = colorScheme.secondary
@Composable
fun MaterialTheme.roomListUnreadIndicator() = colorScheme.primary
val SemanticColors.unreadIndicator
get() = iconAccentTertiary
val SemanticColors.roomListPlaceholder
get() = bgSubtleSecondary
// This color is not present in Semantic color, so put hard-coded value for now
val SemanticColors.messageFromMeBackground
get() = if (isLight) {
// We want LightDesignTokens.colorGray400
Color(0xFFE1E6EC)
} else {
// We want DarkDesignTokens.colorGray500
Color(0xFF323539)
}
// This color is not present in Semantic color, so put hard-coded value for now
val SemanticColors.messageFromOtherBackground
get() = if (isLight) {
// We want LightDesignTokens.colorGray300
Color(0xFFF0F2F5)
} else {
// We want DarkDesignTokens.colorGray400
Color(0xFF26282D)
}
@Preview
@Composable
@ -57,7 +82,10 @@ private fun ContentToPreview() {
"roomListRoomName" to MaterialTheme.roomListRoomName(),
"roomListRoomMessage" to MaterialTheme.roomListRoomMessage(),
"roomListRoomMessageDate" to MaterialTheme.roomListRoomMessageDate(),
"roomListUnreadIndicator" to MaterialTheme.roomListUnreadIndicator(),
"unreadIndicator" to ElementTheme.colors.unreadIndicator,
"roomListPlaceholder" to ElementTheme.colors.roomListPlaceholder,
"messageFromMeBackground" to ElementTheme.colors.messageFromMeBackground,
"messageFromOtherBackground" to ElementTheme.colors.messageFromOtherBackground,
)
)
}

View file

@ -18,95 +18,6 @@ package io.element.android.libraries.designsystem.theme
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.airbnb.android.showkase.annotation.ShowkaseTypography
/**
* TODO Provide the typo to Material3 theme.
*/
@ShowkaseTypography(name = "H1", group = "Element")
val h1Default: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Bold,
fontSize = 24.sp
)
@ShowkaseTypography(name = "Body1", group = "Element")
val body1Default: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
@ShowkaseTypography(name = "BodySmall", group = "Element")
val bodySmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
)
@ShowkaseTypography(name = "bodyMedium", group = "Element")
val bodyMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.Normal,
fontSize = 18.sp
)
@ShowkaseTypography(name = "Body Large", group = "Element")
val bodyLargeDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
@ShowkaseTypography(name = "Headline Small", group = "Element")
val headlineSmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
lineHeight = 30.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "Headline Medium", group = "Element")
val headlineMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 28.sp,
lineHeight = 34.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "Headline Large", group = "Element")
val headlineLargeDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 32.sp,
lineHeight = 38.sp,
letterSpacing = 1.sp
)
@ShowkaseTypography(name = "titleSmall", group = "Element")
val titleSmallDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.5.sp
)
@ShowkaseTypography(name = "titleMedium", group = "Element")
val titleMediumDefault: TextStyle = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 18.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
// Temporary style for text that needs to be aligned without weird font padding issues. `includeFontPadding` will default to false in a future version of
// compose, at which point this can be removed.

View file

@ -0,0 +1,56 @@
/*
* 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.theme.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties
import io.element.android.libraries.theme.ElementTheme
private val minMenuWidth = 200.dp
@Composable
fun DropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
// By default add a 16.dp offset to the menu
offset: DpOffset = DpOffset(x = 16.dp, y = 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
) {
val bgColor = if (ElementTheme.isLightTheme) {
ElementTheme.materialColors.background
} else {
ElementTheme.colors.bgSubtlePrimary
}
androidx.compose.material3.DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier
.background(color = bgColor)
.widthIn(min = minMenuWidth),
offset = offset,
properties = properties,
content = content
)
}

View file

@ -0,0 +1,84 @@
/*
* 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.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.MenuDefaults
import androidx.compose.material3.MenuItemColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.theme.ElementTheme
@Composable
fun DropdownMenuItem(
text: @Composable () -> Unit,
onClick: () -> Unit,
modifier: Modifier = Modifier,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
enabled: Boolean = true,
colors: MenuItemColors = MenuDefaults.itemColors(),
contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
androidx.compose.material3.DropdownMenuItem(
text = text,
onClick = onClick,
modifier = modifier,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,
enabled = enabled,
colors = colors,
contentPadding = contentPadding,
interactionSource = interactionSource
)
}
@Composable
fun DropdownMenuItemText(
text: String,
modifier: Modifier = Modifier,
) {
Text(
text = text,
color = ElementTheme.materialColors.primary,
style = ElementTheme.typography.fontBodyLgRegular,
modifier = modifier,
)
}
@Preview(group = PreviewGroup.Menus)
@Composable
internal fun DropdownMenuItemPreview() = ElementThemedPreview { ContentToPreview() }
@Composable
private fun ContentToPreview() {
DropdownMenuItem(
text = { DropdownMenuItemText(text = "Item") },
onClick = {},
leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) },
trailingIcon = { Icon(Icons.Default.Share, contentDescription = null) },
)
}

View file

@ -56,13 +56,18 @@ fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
// Will be removed, only style should be used
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
// Will be removed, only style should be used
fontWeight: FontWeight? = null,
// Will be removed, only style should be used
fontFamily: FontFamily? = null,
// Will be removed, only style should be used
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
// Will be removed, only style should be used
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
@ -97,13 +102,18 @@ fun Text(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
// Will be removed, only style should be used
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
// Will be removed, only style should be used
fontWeight: FontWeight? = null,
// Will be removed, only style should be used
fontFamily: FontFamily? = null,
// Will be removed, only style should be used
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
// Will be removed, only style should be used
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,

View file

@ -19,8 +19,6 @@ package io.element.android.libraries.designsystem.theme.components.previews
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowRight
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -30,6 +28,9 @@ import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
@ -59,7 +60,7 @@ internal fun MenuPreview() {
null
}
DropdownMenuItem(
text = { Text(text = "Item $i") },
text = { DropdownMenuItemText(text = "Item $i") },
onClick = { isExpanded = false },
leadingIcon = leadingIcon,
trailingIcon = trailingIcon,

View file

@ -16,6 +16,8 @@
package io.element.android.libraries.textcomposer
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@ -44,11 +46,13 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
@ -58,9 +62,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@ -81,8 +85,9 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.android.awaitFrame
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun TextComposer(
composerText: String?,
@ -106,21 +111,31 @@ fun TextComposer(
AttachmentButton(onClick = onAddAttachment, modifier = Modifier.padding(vertical = 6.dp))
Spacer(modifier = Modifier.width(12.dp))
var lineCount by remember { mutableStateOf(0) }
val roundedCorners = remember(lineCount, composerMode) {
val roundedCornerSize = remember(lineCount, composerMode) {
if (lineCount > 1 || composerMode is MessageComposerMode.Special) {
RoundedCornerShape(20.dp)
20.dp
} else {
RoundedCornerShape(28.dp)
28.dp
}
}
val roundedCornerSizeState = animateDpAsState(
targetValue = roundedCornerSize,
animationSpec = tween(
durationMillis = 100,
)
)
val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value)
val minHeight = 42.dp
val bgColor = ElementTheme.colors.bgSubtleSecondary
// Change border color depending on focus
var hasFocus by remember { mutableStateOf(false) }
val borderColor = if (hasFocus) ElementTheme.colors.borderDisabled else bgColor
Column(
modifier = Modifier
.fillMaxWidth()
.clip(roundedCorners)
.background(MaterialTheme.colorScheme.surfaceVariant)
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, roundedCorners)
.background(color = bgColor)
.border(1.dp, borderColor, roundedCorners)
) {
if (composerMode is MessageComposerMode.Special) {
ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
@ -132,7 +147,10 @@ fun TextComposer(
.fillMaxWidth()
.heightIn(min = minHeight)
.focusRequester(focusRequester)
.onFocusEvent { onFocusChanged(it.hasFocus) },
.onFocusEvent {
hasFocus = it.hasFocus
onFocusChanged(it.hasFocus)
},
value = text,
onValueChange = { onComposerTextChange(it) },
onTextLayout = {
@ -156,16 +174,16 @@ fun TextComposer(
colors = TextFieldDefaults.colors(
unfocusedTextColor = MaterialTheme.colorScheme.secondary,
focusedTextColor = MaterialTheme.colorScheme.primary,
unfocusedPlaceholderColor = MaterialTheme.colorScheme.secondary,
focusedPlaceholderColor = MaterialTheme.colorScheme.secondary,
unfocusedPlaceholderColor = ElementTheme.colors.textDisabled,
focusedPlaceholderColor = ElementTheme.colors.textDisabled,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent,
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
errorContainerColor = MaterialTheme.colorScheme.surfaceVariant,
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
unfocusedContainerColor = bgColor,
focusedContainerColor = bgColor,
errorContainerColor = bgColor,
disabledContainerColor = bgColor,
)
)
}
@ -181,6 +199,18 @@ fun TextComposer(
}
}
}
// Request focus when changing mode, and show keyboard.
val keyboard = LocalSoftwareKeyboardController.current
LaunchedEffect(composerMode) {
if (composerMode is MessageComposerMode.Special) {
focusRequester.requestFocus()
keyboard?.let {
awaitFrame()
it.show()
}
}
}
}
@Composable
@ -212,29 +242,35 @@ private fun EditingModeView(
modifier: Modifier = Modifier,
) {
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
horizontalArrangement = Arrangement.spacedBy(5.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp)
.padding(start = 12.dp)
) {
Icon(
resourceId = VectorIcons.Edit,
contentDescription = stringResource(CommonStrings.common_editing),
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(16.dp),
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
.padding(vertical = 8.dp)
.size(16.dp),
)
Text(
stringResource(CommonStrings.common_editing),
style = ElementTextStyles.Regular.caption2,
style = ElementTheme.typography.fontBodySmRegular,
textAlign = TextAlign.Start,
color = MaterialTheme.colorScheme.secondary,
modifier = Modifier.weight(1f)
color = ElementTheme.materialColors.secondary,
modifier = Modifier
.padding(vertical = 8.dp)
.weight(1f)
)
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(CommonStrings.action_close),
tint = MaterialTheme.colorScheme.secondary,
tint = ElementTheme.materialColors.secondary,
modifier = Modifier
.padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 12.dp)
.size(16.dp)
.clickable(
enabled = true,
@ -242,8 +278,7 @@ private fun EditingModeView(
interactionSource = MutableInteractionSource(),
indication = rememberRipple(bounded = false)
),
)
)
}
}
@ -279,17 +314,16 @@ private fun ReplyToModeView(
Text(
text = senderName,
modifier = Modifier.fillMaxWidth(),
style = ElementTextStyles.Regular.caption2.copy(fontWeight = FontWeight.Medium),
style = ElementTheme.typography.fontBodySmMedium,
textAlign = TextAlign.Start,
color = MaterialTheme.colorScheme.primary,
color = ElementTheme.materialColors.primary,
)
Text(
modifier = Modifier.fillMaxWidth(),
text = text.orEmpty(),
style = ElementTextStyles.Regular.caption1,
style = ElementTheme.typography.fontBodyMdRegular,
textAlign = TextAlign.Start,
color = MaterialTheme.colorScheme.secondary,
color = ElementTheme.materialColors.secondary,
maxLines = if (attachmentThumbnailInfo != null) 1 else 2,
overflow = TextOverflow.Ellipsis,
)
@ -316,24 +350,22 @@ private fun AttachmentButton(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(modifier) {
Surface(
Modifier
.size(30.dp)
.clickable(true, onClick = onClick),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary
) {
Image(
modifier = Modifier.size(12.5f.dp),
painter = painterResource(R.drawable.ic_add_attachment),
contentDescription = null,
contentScale = ContentScale.Inside,
colorFilter = ColorFilter.tint(
LocalContentColor.current
)
Surface(
modifier
.size(30.dp)
.clickable(onClick = onClick),
shape = CircleShape,
color = ElementTheme.colors.iconPrimary
) {
Image(
modifier = Modifier.size(12.5f.dp),
painter = painterResource(R.drawable.ic_add_attachment),
contentDescription = null,
contentScale = ContentScale.Inside,
colorFilter = ColorFilter.tint(
LocalContentColor.current
)
}
)
}
}
@ -349,7 +381,7 @@ private fun BoxScope.SendButton(
Box(
modifier = modifier
.clip(CircleShape)
.background(if (canSendMessage) ElementTheme.legacyColors.accentColor else Color.Transparent)
.background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent)
.size(30.dp)
.align(Alignment.BottomEnd)
.applyIf(composerMode !is MessageComposerMode.Edit, ifTrue = {
@ -376,7 +408,8 @@ private fun BoxScope.SendButton(
modifier = Modifier.size(16.dp),
resourceId = iconId,
contentDescription = contentDescription,
tint = if (canSendMessage) Color.White else ElementTheme.legacyColors.quaternary
// Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary
tint = if (canSendMessage) Color.White else ElementTheme.colors.iconDisabled
)
}
}

View file

@ -37,9 +37,6 @@ import io.element.android.libraries.theme.compound.generated.SemanticColors
@Deprecated("Use SemanticColors instead")
@Stable
class ElementColors(
messageFromMeBackground: Color,
messageFromOtherBackground: Color,
messageHighlightedBackground: Color,
quaternary: Color,
quinary: Color,
gray300: Color,
@ -47,13 +44,6 @@ class ElementColors(
placeholder: Color,
isLight: Boolean
) {
var messageFromMeBackground by mutableStateOf(messageFromMeBackground)
private set
var messageFromOtherBackground by mutableStateOf(messageFromOtherBackground)
private set
var messageHighlightedBackground by mutableStateOf(messageHighlightedBackground)
private set
var quaternary by mutableStateOf(quaternary)
private set
@ -73,9 +63,6 @@ class ElementColors(
private set
fun copy(
messageFromMeBackground: Color = this.messageFromMeBackground,
messageFromOtherBackground: Color = this.messageFromOtherBackground,
messageHighlightedBackground: Color = this.messageHighlightedBackground,
quaternary: Color = this.quaternary,
quinary: Color = this.quinary,
gray300: Color = this.gray300,
@ -83,9 +70,6 @@ class ElementColors(
placeholder: Color = this.placeholder,
isLight: Boolean = this.isLight,
) = ElementColors(
messageFromMeBackground = messageFromMeBackground,
messageFromOtherBackground = messageFromOtherBackground,
messageHighlightedBackground = messageHighlightedBackground,
quaternary = quaternary,
quinary = quinary,
gray300 = gray300,
@ -95,9 +79,6 @@ class ElementColors(
)
fun updateColorsFrom(other: ElementColors) {
messageFromMeBackground = other.messageFromMeBackground
messageFromOtherBackground = other.messageFromOtherBackground
messageHighlightedBackground = other.messageHighlightedBackground
quaternary = other.quaternary
quinary = other.quinary
gray300 = other.gray300
@ -108,9 +89,6 @@ class ElementColors(
}
internal fun elementColorsLight() = ElementColors(
messageFromMeBackground = SystemGrey5Light,
messageFromOtherBackground = SystemGrey6Light,
messageHighlightedBackground = Azure,
quaternary = Gray_100,
quinary = Gray_50,
gray300 = LightDesignTokens.colorGray300,
@ -120,9 +98,6 @@ internal fun elementColorsLight() = ElementColors(
)
internal fun elementColorsDark() = ElementColors(
messageFromMeBackground = SystemGrey5Dark,
messageFromOtherBackground = SystemGrey6Dark,
messageHighlightedBackground = Azure,
quaternary = Gray_400,
quinary = Gray_450,
gray300 = DarkDesignTokens.colorGray300,

View file

@ -32,9 +32,6 @@ val LightGrey = Color(0x993C3C43)
@ShowkaseColor(name = "DarkGrey", group = "Material Design")
val DarkGrey = Color(0x99EBEBF5)
val AvatarGradientStart = Color(0xFF4CA1AF)
val AvatarGradientEnd = Color(0xFFC4E0E5)
val SystemGreyLight = Color(0xFF8E8E93)
val SystemGreyDark = Color(0xFF8E8E93)
val SystemGrey2Light = Color(0xFFAEAEB2)

View file

@ -89,7 +89,8 @@ internal val compoundColorsDark = SemanticColors(
textInfoPrimary = DarkDesignTokens.colorBlue900,
textOnSolidPrimary = DarkDesignTokens.colorThemeBg,
bgSubtlePrimary = DarkDesignTokens.colorGray400,
bgSubtleSecondary = DarkDesignTokens.colorBgSubtleSecondaryLevel0,
// The value DarkDesignTokens.colorBgSubtleSecondaryLevel0 is defined to colorThemeBg, this is not correct, so override the value here until this is fixed,
bgSubtleSecondary = DarkDesignTokens.colorGray300, // DarkDesignTokens.colorBgSubtleSecondaryLevel0
bgCanvasDefault = DarkDesignTokens.colorBgCanvasDefaultLevel1,
bgCanvasDisabled = DarkDesignTokens.colorGray200,
bgActionPrimaryRest = DarkDesignTokens.colorGray1400,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14da396544c0accc0229fa249c43caa70d105482d34290edac22047891386ff7
size 39928
oid sha256:db05c9dc303b03a8965e199981cb786491fcf814bf78244b3fc5b7e323807700
size 28097

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:adabf8c147f6122a180db6f2b44fcd85b2b08ec6651acb7b3519e5a821045f4a
size 39902
oid sha256:bdb04dc4e9f047baa6209478c3083c80738e208938d2f25df282fb84dd3aecd0
size 28855

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cc235fc17b0eaaacf1a2dc6ec4759372d29cd4ba0741f7f6f57a9bb9a460c806
size 101302
oid sha256:d8b15997a41e7ba655990243c5347aa87d59811a727b034a7b52e99b074e6648
size 88860

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7a6baba1197b01eb91e2dc6720482b97d9d0cdd5aaffa0d19d3e6b0860dadcf8
size 52502
oid sha256:153fe6422f5248b250096a7ef51a2b89c970f47bca5a24d0938632d08f9e96e2
size 46872

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3eddd53d1e02f314d219ad3c7a935149a973ecf47603a3dd0492053f0e174668
size 35977
oid sha256:e7642a1adb1faec60a301ee86bb34558ea71d15f4fbd3d5ddd5d1661249b725a
size 25759

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9db3469badeb7f71b5f9cb6df6b230158f8d383e48dae2b615e94b885b1f2145
size 81076
oid sha256:b0457439922b1990f6fd72449c6f4042b72755e09ab9f3c9c8a8c03c9fc925eb
size 63888

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ab609e664a3fb0f258b99414b799e591161876d348d1c35f03ece1a57d6dce1
size 91478
oid sha256:98c56ef886a3d1b6b3109bb835e587176f4e420bb05057dc150c82153605f7d1
size 67794

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e00be325947e9ef638fdabd178b70da994a5b78f82ebfa199d469883b156e8bb
size 35205
oid sha256:f467065bb9c78d0e95a8a688bfc50edce522b2832c9a53e5a68899e843a988ca
size 25859

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f85f0a3b5bb3edd6d864a2a211437a278f6bb7a9b66367a9ed4d596f673dc197
size 80258
oid sha256:a2ba6ed9981ea1ad3f0cdf76d600d6a51daf58a04f15c5bf23713c7f31e84b94
size 65471

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8defc1027b2a97aeca89c7f969d458afbcbb4f0ed3e4474046e1242ceec8d80d
size 90196
oid sha256:c1f75b3ada8d9462a23b7defd26ac16562ff6ccf9babfcc7330286712ceb805e
size 69581

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5847edfd77fe888d3da67fc27c72a9dd40b78ebede21483a48394ee5859197a7
size 93560
oid sha256:8d04e77e3a7e7862f29868be068df658382ed7dec94fcea86665bbd5074591bd
size 82804

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b42685b2852d5d45eee744a98dba74a5f26ddc1fb88960a7c23d4e335a75369
size 95600
oid sha256:4f8de89448ff5f4f95c7611657e38e0ba75c2f503b82fb04f584f3116353ae51
size 85954

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8b275a7d15d76711cc2eaa00e819a573e50ddff699f82b2b0e65ec8489547732
size 22163
oid sha256:b2567be3b27bb2ea979b7da492fb059e87b25c5938c07aacaa621a97685bacd5
size 21095

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5b7247bd672cf5a4ed121d65369c0825a01be379773ca696038eadf4f23269b7
size 27606
oid sha256:300883c3e49ff5e655952cf2b5a0a194718fde9c6fd289c3125e4cd3609cea00
size 26379

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6c09f5e6fb876196e5b6db3ea0f562a2ff04c48a5858a939f2765d41b1c4e13
size 22314
oid sha256:e80d2a64eb4c39e117dbd3bdd6b1b45b19a99f8e1ccb905547f82096fae8510d
size 21465

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2eca0c7ce88cf3ccb13fc1a7f52c96922c417f8cfb951f25db91c20c1714771a
size 28472
oid sha256:1b91f0e2d72bf5b12430183ccb98cb30e1e0ab3859472c5a6368b5cca1ceb33c
size 27574

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d4479241e4fc5c15ec6c2122d1eaafd9f2fe79a9235fd25f215f850e0b852f03
size 31269
oid sha256:4233e67a2f27e3e73924b9af5f43d0e877a397f4a2ee3f8d436a713e1d2b3c0c
size 28738

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9ea5f4da6a888551e2e91a8b594fcc090cf806a56df315c5607abcb5e4f12495
size 36839
oid sha256:69b2702985d50fedbb720e86eef63c2734e328d8537c6a8f644fcf75c7e710a8
size 32883

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f58fe221866c9d199645ef71cc5d38aa1fef67e49f74bed5e042ae5ce1f3a5d
size 36803
oid sha256:76681a4959008b8646273424cd50566d7c868620938e07849da596ccd3172781
size 33020

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa16a4c974eaee9e02b734db508ab04f4ff87ef6d22ae64d0841e63ad37967e3
size 17760
oid sha256:0052281199753556fd2d8ed411360ee15e5019e2ddd3ec2b1a9695c81be3a233
size 14402

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0601334a2e45adfd5488e99175156a0e14fdb83fcc292173812fd4232ccc528
size 31374
oid sha256:57878214b3949c4eaa782f763095e9e8234a979e0e7fe83fcf1e5083bfb32bf0
size 28878

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c13288bd03da93cdbc6969ef4b9a0a317da9802a1b02ce41cf305b42146a1f46
size 32673
oid sha256:9482e9994ab5c2367d52811fa72559d9c1660e517b5d242e7d714f3e84ba03c5
size 29271

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0457a0275d880dc738fb60ae9e53b3d1d7630592d2107a0dd03b3cb80685b0cc
size 37881
oid sha256:9bb46ebec6a9fbdab74f9634d1c834c52c1f690183b3b360e24c79012ed3a192
size 34823

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e0ae1dba87aa070607268c0243f18301c7ebf0b23cebe91bc1b668eda9da0025
size 37867
oid sha256:fca36d620adf924e4c6605832dea8df4f2b87e7ac472c98a5ffae0056816be8a
size 34944

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0bde7c2be2ae19012c6f8df9626bc0d912e2899239fe5527865980dbe02cfee7
size 17384
oid sha256:9b202f4160bdb58a9e87cfc66f11a5aaf66a90591c978dfc797b3cf351c94103
size 14396

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:79a9749e15d3875d95602a325f6238ad666d7acfa9256d6865c1b6492ec74cdf
size 32815
oid sha256:afc363e9509019637ad9a7b4120462e1b2679d02a4b815c5840a83336cb9616a
size 29654

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30856dd6b233c75f387d9160ee3700c21e197f1a17b85da8b654042a1b88601c
size 63741
oid sha256:6ab4fab7980768994954a4089ff32116f28ec46f89ed30474ab45239d39f448c
size 52948

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:466ef043bc10bc265c928e12464c0d42007dd3f15dbca8f7e352169754c58ee2
size 46836
oid sha256:20be7d313c5058ce7016ec573a9db05387df0dc4c41aab07200a2c3f55b35d55
size 43832

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f3bfe2e04da74cb561dddb85cdb36c31ebee2e05ab96fc821bb7d26133826d52
size 47264
oid sha256:c6db133ab25a03de88c6153ce6adc58c834539b2c85e35c1438bb55cdb91abe5
size 44271

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1657e4438b39cbf720e2ec0d488a40e841e9c2b518e4f663dfe7cde0f9c862c2
size 42308
oid sha256:bd2fd38fd3cf6eaa78de876cace7c614680108c3ed0eeb634ebaf9e004840f98
size 37575

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1657e4438b39cbf720e2ec0d488a40e841e9c2b518e4f663dfe7cde0f9c862c2
size 42308
oid sha256:bd2fd38fd3cf6eaa78de876cace7c614680108c3ed0eeb634ebaf9e004840f98
size 37575

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dae7690e7f01647a4711b3940ca15549fe7ecf9043dd6ef2066b144561398608
size 64708
oid sha256:04357ef36543729e5ed5794f3624194315080b9a5bc6024235a131e009e5a922
size 55391

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b1407a9001ed72fcc8bbedafcdc9233d9c3e2bc7996fd8a4e549c4e7f8d6e1d
size 48179
oid sha256:506d061e8004745bf7c1472d1a96702ef9b47e60e99839e7cab8f16071b88b1d
size 45813

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:630eb1ea4c3979e6e6ad4fcc48eaea723798c85ffb703670566957e42385ae98
size 48616
oid sha256:3f58db0ddc56fc3e6178f4aafbdd0501a5bba129068420cab0c3b8cfadc33b50
size 46244

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66fc42a41b1e0c3abf2edb465f986f722d3558179bc3ebcbf0740e9bc794d62c
size 43023
oid sha256:289b4fb5dc4c0a27deec988f8810584162451c763760d1e9ee66da367f869c6e
size 39173

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66fc42a41b1e0c3abf2edb465f986f722d3558179bc3ebcbf0740e9bc794d62c
size 43023
oid sha256:289b4fb5dc4c0a27deec988f8810584162451c763760d1e9ee66da367f869c6e
size 39173

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c3a97e67ebd54d043dfb09c773ab74e927c48517ce07cd87452c69bb96cb228
size 38498
oid sha256:5ce2edea622636c3709d7da3d01c931a2ced65d31738d9cfc9e5f318a374ca30
size 37850

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af4ab5ef652b71f295d02d69f19e8dca81ef1929c6b6c5b85f66b53da62811fb
size 43427
oid sha256:aa58a3f5fa6ee6e9a7f89f87a64b7b43577fca75cd44193b521875c8d6a184e3
size 43823

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f28ac645ba2e330a8cdbdc8e26040a1f3c02e0e647975bfbed5e7dc175bd372b
size 43769
oid sha256:50cbc5955836725e0f58f2058e2c43cce0639c122036470fac58b89f3208c7f2
size 44139

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e32044f02911fa178f7030eab11ccd04bd02f82811eb86aeb585d5116e76d75
size 37682
oid sha256:e6787391e332dcf4dd0998c18447962a1ee03314e276f6b7d0a6bd6717f30f43
size 38214

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:079c29fb8bc0bc1d8c8899a0ec13d997127d57d5d49ad04906247038f8b39d2f
size 39196
oid sha256:acaa3c96e40cd2d2d0cace906e87a6e36801a0836b1eff059a2f0a031e41ddd8
size 38770

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b5623e3f37e2aa89cdce2a8d1181c8f417fa05917bf62fc46b541b86d140a780
size 44611
oid sha256:b0ac4827780ad236721b793b1d07a3f7ee514811ca3d8f29c8d447cdb99e739e
size 45293

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ce0bbe6be7ede759386ef4c56afef4f419ee0c6264bc80b22a3a2b563a170807
size 45096
oid sha256:57f7684c300496d7c8ca31e500b2191b85e3bc2ecbce489e77f1c71edec3d877
size 45790

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9ccef4dc0eed428153c228253c198dcfb7ff5c18d6e6a44d31dff6ac548f4f61
size 38558
oid sha256:fb973d123139c6da77e5b764cbca51866310ea81caf7057c584064677d2deff8
size 39233

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b75570c52aeecb3a377dbf54cc53b439b040ddd66f44b18ae358ee61887067bf
size 30316
oid sha256:245701b0b6fbb5f886af725435ea62ac2ed2164ae51cc74ed3560c49d5692144
size 26585

Some files were not shown because too many files have changed in this diff Show more