Merge remote-tracking branch 'origin/develop' into feature/cjs/view-location-in-timeline
This commit is contained in:
commit
005b22391f
454 changed files with 2400 additions and 1234 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -41,6 +42,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
fun ProgressDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String? = null,
|
||||
type: ProgressDialogType = ProgressDialogType.Indeterminate,
|
||||
onDismiss: () -> Unit = {},
|
||||
) {
|
||||
Dialog(
|
||||
|
|
@ -50,14 +52,40 @@ fun ProgressDialog(
|
|||
ProgressDialogContent(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
progressIndicator = {
|
||||
when (type) {
|
||||
is ProgressDialogType.Indeterminate -> {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
is ProgressDialogType.Determinate -> {
|
||||
CircularProgressIndicator(
|
||||
progress = type.progress,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed interface ProgressDialogType {
|
||||
data class Determinate(val progress: Float) : ProgressDialogType
|
||||
object Indeterminate : ProgressDialogType
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProgressDialogContent(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String? = null,
|
||||
progressIndicator: @Composable () -> Unit = {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -71,9 +99,7 @@ private fun ProgressDialogContent(
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(top = 38.dp, bottom = 32.dp, start = 40.dp, end = 40.dp)
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
progressIndicator()
|
||||
if (!text.isNullOrBlank()) {
|
||||
Spacer(modifier = Modifier.height(22.dp))
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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) },
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ interface MatrixTimeline {
|
|||
val canBackPaginate: Boolean
|
||||
)
|
||||
|
||||
fun paginationState(): StateFlow<PaginationState>
|
||||
fun timelineItems(): Flow<List<MatrixTimelineItem>>
|
||||
val paginationState: StateFlow<PaginationState>
|
||||
val timelineItems: Flow<List<MatrixTimelineItem>>
|
||||
|
||||
suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit>
|
||||
suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun login(username: String, password: String): Result<SessionId> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val client = authService.login(username, password, "ElementX Android", null)
|
||||
val client = authService.login(username, password, "Element X Android", null)
|
||||
val sessionData = client.use { it.session().toSessionData() }
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import kotlinx.coroutines.FlowPreview
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.PaginationOptions
|
||||
|
|
@ -45,10 +46,10 @@ class RustMatrixTimeline(
|
|||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
) : MatrixTimeline {
|
||||
|
||||
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>> =
|
||||
private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> =
|
||||
MutableStateFlow(emptyList())
|
||||
|
||||
private val paginationState = MutableStateFlow(
|
||||
private val _paginationState = MutableStateFlow(
|
||||
MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false)
|
||||
)
|
||||
|
||||
|
|
@ -64,19 +65,15 @@ class RustMatrixTimeline(
|
|||
)
|
||||
|
||||
private val timelineDiffProcessor = MatrixTimelineDiffProcessor(
|
||||
paginationState = paginationState,
|
||||
timelineItems = timelineItems,
|
||||
paginationState = _paginationState,
|
||||
timelineItems = _timelineItems,
|
||||
timelineItemFactory = timelineItemFactory,
|
||||
)
|
||||
|
||||
override fun paginationState(): StateFlow<MatrixTimeline.PaginationState> {
|
||||
return paginationState
|
||||
}
|
||||
override val paginationState: StateFlow<MatrixTimeline.PaginationState> = _paginationState.asStateFlow()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun timelineItems(): Flow<List<MatrixTimelineItem>> {
|
||||
return timelineItems.sample(50)
|
||||
}
|
||||
override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems.sample(50)
|
||||
|
||||
internal suspend fun postItems(items: List<TimelineItem>) {
|
||||
timelineDiffProcessor.postItems(items)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
|||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.File
|
||||
|
|
@ -78,6 +79,7 @@ class FakeMatrixRoom(
|
|||
private var forwardEventResult = Result.success(Unit)
|
||||
private var reportContentResult = Result.success(Unit)
|
||||
private var sendLocationResult = Result.success(Unit)
|
||||
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
|
||||
|
||||
var sendMediaCount = 0
|
||||
private set
|
||||
|
|
@ -152,7 +154,7 @@ class FakeMatrixRoom(
|
|||
return toggleReactionResult
|
||||
}
|
||||
|
||||
if(_myReactions.contains(emoji)) {
|
||||
if (_myReactions.contains(emoji)) {
|
||||
_myReactions.remove(emoji)
|
||||
} else {
|
||||
_myReactions.add(emoji)
|
||||
|
|
@ -229,20 +231,26 @@ class FakeMatrixRoom(
|
|||
thumbnailFile: File,
|
||||
imageInfo: ImageInfo,
|
||||
progressCallback: ProgressCallback?
|
||||
): Result<Unit> = fakeSendMedia()
|
||||
): Result<Unit> = fakeSendMedia(progressCallback)
|
||||
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia()
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia(
|
||||
progressCallback
|
||||
)
|
||||
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia()
|
||||
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia(progressCallback)
|
||||
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia()
|
||||
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<Unit> = fakeSendMedia(progressCallback)
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, rooms: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventResult
|
||||
}
|
||||
|
||||
private suspend fun fakeSendMedia(): Result<Unit> = simulateLongTask {
|
||||
private suspend fun fakeSendMedia(progressCallback: ProgressCallback?): Result<Unit> = simulateLongTask {
|
||||
sendMediaResult.onSuccess {
|
||||
progressCallbackValues.forEach { (current, total) ->
|
||||
progressCallback?.onProgress(current, total)
|
||||
delay(1)
|
||||
}
|
||||
sendMediaCount++
|
||||
}
|
||||
}
|
||||
|
|
@ -381,4 +389,8 @@ class FakeMatrixRoom(
|
|||
fun givenSendLocationResult(result: Result<Unit>) {
|
||||
sendLocationResult = result
|
||||
}
|
||||
|
||||
fun givenProgressCallbackValues(values: List<Pair<Long, Long>>) {
|
||||
progressCallbackValues = values
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,33 +23,30 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
|
||||
class FakeMatrixTimeline(
|
||||
initialTimelineItems: List<MatrixTimelineItem> = emptyList(),
|
||||
initialPaginationState: MatrixTimeline.PaginationState = MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false)
|
||||
) : MatrixTimeline {
|
||||
|
||||
private val paginationState: MutableStateFlow<MatrixTimeline.PaginationState> = MutableStateFlow(initialPaginationState)
|
||||
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = MutableStateFlow(initialTimelineItems)
|
||||
private val _paginationState: MutableStateFlow<MatrixTimeline.PaginationState> = MutableStateFlow(initialPaginationState)
|
||||
private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = MutableStateFlow(initialTimelineItems)
|
||||
|
||||
var sendReadReceiptCount = 0
|
||||
private set
|
||||
|
||||
fun updatePaginationState(update: (MatrixTimeline.PaginationState.() -> MatrixTimeline.PaginationState)) {
|
||||
paginationState.value = update(paginationState.value)
|
||||
_paginationState.getAndUpdate(update)
|
||||
}
|
||||
|
||||
fun updateTimelineItems(update: (items: List<MatrixTimelineItem>) -> List<MatrixTimelineItem>) {
|
||||
timelineItems.value = update(timelineItems.value)
|
||||
_timelineItems.getAndUpdate(update)
|
||||
}
|
||||
|
||||
override fun paginationState(): StateFlow<MatrixTimeline.PaginationState> {
|
||||
return paginationState
|
||||
}
|
||||
override val paginationState: StateFlow<MatrixTimeline.PaginationState> = _paginationState
|
||||
|
||||
override fun timelineItems(): Flow<List<MatrixTimelineItem>> {
|
||||
return timelineItems
|
||||
}
|
||||
override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems
|
||||
|
||||
override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> {
|
||||
updatePaginationState {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.mediaupload.api
|
|||
|
||||
import android.net.Uri
|
||||
import io.element.android.libraries.core.extensions.flatMap
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -26,7 +27,12 @@ class MediaSender @Inject constructor(
|
|||
private val room: MatrixRoom,
|
||||
) {
|
||||
|
||||
suspend fun sendMedia(uri: Uri, mimeType: String, compressIfPossible: Boolean): Result<Unit> {
|
||||
suspend fun sendMedia(
|
||||
uri: Uri,
|
||||
mimeType: String,
|
||||
compressIfPossible: Boolean,
|
||||
progressCallback: ProgressCallback? = null
|
||||
): Result<Unit> {
|
||||
return preProcessor
|
||||
.process(
|
||||
uri = uri,
|
||||
|
|
@ -35,12 +41,13 @@ class MediaSender @Inject constructor(
|
|||
compressIfPossible = compressIfPossible
|
||||
)
|
||||
.flatMap { info ->
|
||||
room.sendMedia(info)
|
||||
room.sendMedia(info, progressCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MatrixRoom.sendMedia(
|
||||
info: MediaUploadInfo,
|
||||
progressCallback: ProgressCallback?
|
||||
): Result<Unit> {
|
||||
return when (info) {
|
||||
is MediaUploadInfo.Image -> {
|
||||
|
|
@ -48,7 +55,7 @@ class MediaSender @Inject constructor(
|
|||
file = info.file,
|
||||
thumbnailFile = info.thumbnailFile,
|
||||
imageInfo = info.info,
|
||||
progressCallback = null
|
||||
progressCallback = progressCallback
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +64,7 @@ class MediaSender @Inject constructor(
|
|||
file = info.file,
|
||||
thumbnailFile = info.thumbnailFile,
|
||||
videoInfo = info.info,
|
||||
progressCallback = null
|
||||
progressCallback = progressCallback
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +72,7 @@ class MediaSender @Inject constructor(
|
|||
sendFile(
|
||||
file = info.file,
|
||||
fileInfo = info.info,
|
||||
progressCallback = null
|
||||
progressCallback = progressCallback
|
||||
)
|
||||
}
|
||||
else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $info"))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue