android, desktop: chat list item layout (#4458)

* android, desktop: chat list item layout

* changes

* changes

* removed background

* paddings

* returned aligning
This commit is contained in:
Stanislav Dmitrenko
2024-07-15 20:21:18 +07:00
committed by GitHub
parent 0847b725b3
commit c6e5d6e2b8
7 changed files with 96 additions and 77 deletions
@@ -12,6 +12,7 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.text.font.FontStyle
@@ -95,7 +96,9 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
if (newChatSheetState.value.isVisible()) hideNewChatSheet(true) else showNewChatSheet()
}
},
Modifier.padding(end = DEFAULT_PADDING - 16.dp + endPadding, bottom = DEFAULT_PADDING - 16.dp).size(AppBarHeight * fontSizeSqrtMultiplier),
Modifier
.padding(end = DEFAULT_PADDING - 16.dp + endPadding, bottom = DEFAULT_PADDING - 16.dp)
.size(AppBarHeight * fontSizeSqrtMultiplier),
elevation = FloatingActionButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
@@ -25,6 +25,7 @@ import chat.simplex.common.views.chat.item.MarkdownText
import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.*
import chat.simplex.common.model.GroupInfo
import chat.simplex.common.platform.appPlatform
import chat.simplex.common.platform.chatModel
import chat.simplex.common.views.chat.item.markedDeletedText
import chat.simplex.res.MR
@@ -47,11 +48,10 @@ fun ChatPreviewView(
@Composable
fun inactiveIcon() {
val sp18 = with(LocalDensity.current) { 18.sp.toDp() }
Icon(
painterResource(MR.images.ic_cancel_filled),
stringResource(MR.strings.icon_descr_group_inactive),
Modifier.size(sp18).background(MaterialTheme.colors.background, CircleShape),
Modifier.size(18.sp.toDp()).background(MaterialTheme.colors.background, CircleShape),
tint = MaterialTheme.colors.secondary
)
}
@@ -88,8 +88,7 @@ fun ChatPreviewView(
@Composable
fun VerifiedIcon() {
val sp19 = with(LocalDensity.current) { 19.sp.toDp() }
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(sp19).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(19.sp.toDp()).padding(end = 3.sp.toDp(), top = 1.sp.toDp()), tint = MaterialTheme.colors.secondary)
}
fun messageDraft(draft: ComposeState, sp20: Dp): Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>> {
@@ -193,7 +192,12 @@ fun ChatPreviewView(
senderBold = true,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body1.copy(color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight, lineHeight = 22.sp),
style = TextStyle(
fontFamily = Inter,
fontSize = 15.sp,
color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight,
lineHeight = 21.sp
),
inlineContent = inlineTextContent,
modifier = Modifier.fillMaxWidth(),
)
@@ -223,11 +227,10 @@ fun ChatPreviewView(
@Composable
fun progressView() {
val sp15 = with(LocalDensity.current) { 15.sp.toDp() }
CircularProgressIndicator(
Modifier
.padding(horizontal = 2.dp)
.size(sp15),
.size(15.sp.toDp())
.offset(y = 2.sp.toDp()),
color = MaterialTheme.colors.secondary,
strokeWidth = 1.5.dp
)
@@ -235,7 +238,6 @@ fun ChatPreviewView(
@Composable
fun chatStatusImage() {
val sp19 = with(LocalDensity.current) { 19.sp.toDp() }
if (cInfo is ChatInfo.Direct) {
if (cInfo.contact.active && cInfo.contact.activeConn != null) {
val descr = contactNetworkStatus?.statusString
@@ -249,7 +251,8 @@ fun ChatPreviewView(
contentDescription = descr,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(sp19)
.size(19.sp.toDp())
.offset(x = 2.sp.toDp())
)
else ->
@@ -272,18 +275,17 @@ fun ChatPreviewView(
Row {
Box(contentAlignment = Alignment.BottomEnd) {
ChatInfoImage(cInfo, size = 72.dp * fontSizeSqrtMultiplier)
Box(Modifier.padding(end = 6.dp, bottom = 6.dp)) {
Box(Modifier.padding(end = 6.sp.toDp(), bottom = 6.sp.toDp())) {
chatPreviewImageOverlayIcon()
}
}
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(start = 8.dp, end = 8.sp.toDp())
.weight(1F)
) {
chatPreviewTitle()
val height = with(LocalDensity.current) { 46.sp.toDp() }
Row(Modifier.heightIn(min = height)) {
Row(Modifier.heightIn(min = 46.sp.toDp()).padding(top = 3.sp.toDp())) {
chatPreviewText()
}
}
@@ -292,67 +294,50 @@ fun ChatPreviewView(
contentAlignment = Alignment.TopEnd
) {
val ts = chat.chatItems.lastOrNull()?.timestampText ?: getTimestampText(chat.chatInfo.chatTs)
Text(
ts,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
ChatListTimestampView(ts)
val n = chat.chatStats.unreadCount
val showNtfsIcon = !chat.chatInfo.ntfsEnabled && (chat.chatInfo is ChatInfo.Direct || chat.chatInfo is ChatInfo.Group)
val sp17 = with(LocalDensity.current) { 17.sp.toDp() }
val sp21 = with(LocalDensity.current) { 21.sp.toDp() }
val sp23 = with(LocalDensity.current) { 23.sp.toDp() }
val sp46 = with(LocalDensity.current) { 46.sp.toDp() }
if (n > 0 || chat.chatStats.unreadChat) {
Box(
Modifier.padding(top = sp23, end = with(LocalDensity.current) { 3.sp.toDp() }),
contentAlignment = Alignment.Center
) {
Modifier.padding(top = 24.5.sp.toDp())) {
Text(
if (n > 0) unreadCountStr(n) else "",
color = Color.White,
fontSize = 11.sp,
fontSize = 10.sp,
modifier = Modifier
.background(if (disabled || showNtfsIcon) MaterialTheme.colors.secondary else MaterialTheme.colors.primaryVariant, shape = CircleShape)
.badgeLayout()
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp)
.padding(horizontal = 3.sp.toDp())
.padding(vertical = 1.sp.toDp())
)
}
} else if (showNtfsIcon) {
Box(
Modifier.padding(top = sp21),
contentAlignment = Alignment.Center
) {
Modifier.padding(top = 22.sp.toDp())) {
Icon(
painterResource(MR.images.ic_notifications_off_filled),
contentDescription = generalGetString(MR.strings.notifications),
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp)
.size(sp17)
.size(17.sp.toDp())
.offset(x = 2.5.sp.toDp())
)
}
} else if (chat.chatInfo.chatSettings?.favorite == true) {
Box(
Modifier.padding(top = sp21),
contentAlignment = Alignment.Center
) {
Modifier.padding(top = 20.sp.toDp())) {
Icon(
painterResource(MR.images.ic_star_filled),
contentDescription = generalGetString(MR.strings.favorite_chat),
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.padding(horizontal = 3.dp)
.padding(vertical = 1.dp)
.size(sp17)
.size(20.sp.toDp())
.offset(x = 2.5.sp.toDp())
)
}
}
Box(
Modifier.padding(top = sp46),
Modifier.padding(top = 46.sp.toDp()),
contentAlignment = Alignment.Center
) {
chatStatusImage()
@@ -364,13 +349,13 @@ fun ChatPreviewView(
@Composable
fun IncognitoIcon(incognito: Boolean) {
if (incognito) {
val sp21 = with(LocalDensity.current) { 21.sp.toDp() }
Icon(
painterResource(MR.images.ic_theater_comedy),
contentDescription = null,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(sp21)
.size(21.sp.toDp())
.offset(x = 1.sp.toDp())
)
}
}
@@ -388,6 +373,23 @@ fun unreadCountStr(n: Int): String {
return if (n < 1000) "$n" else "${n / 1000}" + stringResource(MR.strings.thousand_abbreviation)
}
@Composable fun ChatListTimestampView(ts: String) {
Box(contentAlignment = Alignment.BottomStart) {
// This should be the same font style as in title to make date located on the same line as title
Text(
" ",
style = MaterialTheme.typography.h3,
fontWeight = FontWeight.Bold,
)
Text(
ts,
Modifier.padding(bottom = 5.sp.toDp()).offset(x = if (appPlatform.isDesktop) 1.5.sp.toDp() else 0.dp),
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2.copy(fontSize = 13.sp),
)
}
}
@Preview/*(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
@@ -1,5 +1,6 @@
package chat.simplex.common.views.chatlist
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -7,25 +8,26 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.ProfileImage
import chat.simplex.common.model.PendingContactConnection
import chat.simplex.common.model.getTimestampText
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
@Composable
fun ContactConnectionView(contactConnection: PendingContactConnection) {
Row {
Box(Modifier.size(72.dp), contentAlignment = Alignment.Center) {
ProfileImage(size = 54.dp, null, if (contactConnection.initiated) MR.images.ic_add_link else MR.images.ic_link)
Box(Modifier.size(72.dp * fontSizeSqrtMultiplier), contentAlignment = Alignment.Center) {
ProfileImage(size = 54.dp * fontSizeSqrtMultiplier, null, if (contactConnection.initiated) MR.images.ic_add_link else MR.images.ic_link)
}
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(start = 8.dp / fontSizeSqrtMultiplier, end = 8.sp.toDp())
.weight(1F)
) {
Text(
@@ -36,21 +38,25 @@ fun ContactConnectionView(contactConnection: PendingContactConnection) {
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.secondary
)
val height = with(LocalDensity.current) { 46.sp.toDp() }
Text(contactConnection.description, Modifier.heightIn(min = height), maxLines = 2, color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight)
Text(
contactConnection.description,
Modifier.heightIn(min = 46.sp.toDp()).padding(top = 3.sp.toDp()),
maxLines = 2,
style = TextStyle(
fontFamily = Inter,
fontSize = 15.sp,
color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight,
lineHeight = 21.sp
)
)
}
Box(
contentAlignment = Alignment.TopEnd
) {
val ts = getTimestampText(contactConnection.updatedAt)
Text(
ts,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
ChatListTimestampView(ts)
Box(
Modifier.padding(top = 50.dp),
Modifier.padding(top = 50.sp.toDp()),
contentAlignment = Alignment.Center
) {
IncognitoIcon(contactConnection.incognito)
@@ -6,24 +6,25 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.ChatInfoImage
import chat.simplex.common.model.ChatInfo
import chat.simplex.common.model.getTimestampText
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
@Composable
fun ContactRequestView(contactRequest: ChatInfo.ContactRequest) {
Row {
ChatInfoImage(contactRequest, size = 72.dp)
ChatInfoImage(contactRequest, size = 72.dp * fontSizeSqrtMultiplier)
Column(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(start = 8.dp, end = 8.sp.toDp())
.weight(1F)
) {
Text(
@@ -32,21 +33,21 @@ fun ContactRequestView(contactRequest: ChatInfo.ContactRequest) {
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h3,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colors.primary
color = MaterialTheme.colors.primary,
)
Text(
stringResource(MR.strings.contact_wants_to_connect_with_you),
Modifier.heightIn(min = 46.sp.toDp()).padding(top = 3.sp.toDp()),
maxLines = 2,
style = TextStyle(
fontFamily = Inter,
fontSize = 15.sp,
color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight,
lineHeight = 21.sp
)
)
val height = with(LocalDensity.current) { 46.sp.toDp() }
Text(stringResource(MR.strings.contact_wants_to_connect_with_you), Modifier.heightIn(min = height), maxLines = 2, color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight)
}
val ts = getTimestampText(contactRequest.contactRequest.updatedAt)
Column(
Modifier.fillMaxHeight(),
) {
Text(
ts,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
}
ChatListTimestampView(ts)
}
}
@@ -284,7 +284,7 @@ fun UserProfilePickerItem(
Text(
unreadCountStr(unreadCount),
color = Color.White,
fontSize = 11.sp,
fontSize = 10.sp,
modifier = Modifier
.background(MaterialTheme.colors.primaryVariant, shape = CircleShape)
.padding(2.dp)
@@ -12,6 +12,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.layout.ContentScale
@@ -27,7 +28,7 @@ import dev.icerock.moko.resources.ImageResource
import kotlin.math.max
@Composable
fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme.colors.secondaryVariant) {
fun ChatInfoImage(chatInfo: ChatInfo, size: Dp, iconColor: Color = MaterialTheme.colors.secondaryVariant, shadow: Boolean = false) {
val icon =
when (chatInfo) {
is ChatInfo.Group -> MR.images.ic_supervised_user_circle_filled
@@ -529,6 +529,12 @@ val fontSizeSqrtMultiplier: Float
val desktopDensityScaleMultiplier: Float
@Composable get() = if (appPlatform.isDesktop) remember { appPrefs.densityScale.state }.value else 1f
@Composable
fun TextUnit.toDp(): Dp {
check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
return Dp(value * LocalDensity.current.fontScale)
}
@Composable
fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenGone: () -> Unit) {
DisposableEffect(Unit) {