mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-14 21:15:37 +00:00
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:
committed by
GitHub
parent
0847b725b3
commit
c6e5d6e2b8
+4
-1
@@ -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,
|
||||
|
||||
+47
-45
@@ -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,
|
||||
|
||||
+19
-13
@@ -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)
|
||||
|
||||
+17
-16
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+6
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user