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
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,

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