mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-06-07 21:53:29 +00:00
kotlin
This commit is contained in:
@@ -2813,6 +2813,7 @@ data class ChatItem (
|
||||
is CIContent.RcvDirectE2EEInfo -> false
|
||||
is CIContent.SndGroupE2EEInfo -> false
|
||||
is CIContent.RcvGroupE2EEInfo -> false
|
||||
is CIContent.ChatBanner -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
@@ -2879,6 +2880,7 @@ data class ChatItem (
|
||||
is CIContent.RcvDirectE2EEInfo -> false
|
||||
is CIContent.SndGroupE2EEInfo -> false
|
||||
is CIContent.RcvGroupE2EEInfo -> false
|
||||
is CIContent.ChatBanner -> false
|
||||
is CIContent.InvalidJSON -> false
|
||||
}
|
||||
|
||||
@@ -3549,6 +3551,7 @@ sealed class CIContent: ItemContent {
|
||||
@Serializable @SerialName("rcvDirectE2EEInfo") class RcvDirectE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndGroupE2EEInfo") class SndGroupE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupE2EEInfo") class RcvGroupE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("chatBanner") object ChatBanner: CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("invalidJSON") data class InvalidJSON(val json: String): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
|
||||
override val text: String get() = when (this) {
|
||||
@@ -3582,6 +3585,7 @@ sealed class CIContent: ItemContent {
|
||||
is RcvDirectE2EEInfo -> directE2EEInfoStr(e2eeInfo)
|
||||
is SndGroupE2EEInfo -> e2eeInfoNoPQStr
|
||||
is RcvGroupE2EEInfo -> e2eeInfoNoPQStr
|
||||
is ChatBanner -> ""
|
||||
is InvalidJSON -> "invalid data"
|
||||
}
|
||||
|
||||
|
||||
+161
-32
@@ -1754,6 +1754,130 @@ fun BoxScope.ChatItemsList(
|
||||
ChatItemView(cItem, range, itemSeparation, previousItemSeparationLargeGap)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatBannerView() {
|
||||
fun chatContext(): String? {
|
||||
return when (chatInfo) {
|
||||
is ChatInfo.Direct -> {
|
||||
val contact = chatInfo.contact
|
||||
val preparedLinkType = contact.preparedContact?.uiConnLinkType
|
||||
if (contact.nextConnectPrepared && preparedLinkType != null) {
|
||||
when (preparedLinkType) {
|
||||
ConnectionMode.Inv -> generalGetString(MR.strings.chat_banner_1_time_invitation)
|
||||
ConnectionMode.Con -> generalGetString(MR.strings.chat_banner_contact_address)
|
||||
}
|
||||
} else if (contact.nextAcceptContactRequest) {
|
||||
generalGetString(MR.strings.chat_banner_contact_request)
|
||||
} else {
|
||||
generalGetString(MR.strings.chat_banner_contact)
|
||||
}
|
||||
}
|
||||
|
||||
is ChatInfo.Group -> {
|
||||
val groupInfo = chatInfo.groupInfo
|
||||
when (groupInfo.businessChat?.chatType) {
|
||||
null -> {
|
||||
if (groupInfo.membership.memberStatus == GroupMemberStatus.MemCreator) {
|
||||
generalGetString(MR.strings.chat_banner_your_group)
|
||||
} else {
|
||||
generalGetString(MR.strings.chat_banner_group)
|
||||
}
|
||||
}
|
||||
|
||||
BusinessChatType.Business -> generalGetString(MR.strings.chat_banner_business)
|
||||
BusinessChatType.Customer -> generalGetString(MR.strings.chat_banner_customer)
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.colors.background
|
||||
) {
|
||||
val bannerModifier = if (appPlatform.isDesktop) Modifier.width(400.dp) else Modifier.fillMaxWidth()
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = bannerModifier
|
||||
.padding(DEFAULT_PADDING)
|
||||
) {
|
||||
ChatInfoImage(chatInfo, size = 96.dp)
|
||||
|
||||
Text(
|
||||
chatInfo.displayName,
|
||||
style = MaterialTheme.typography.h3,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.widthIn(max = 240.dp)
|
||||
)
|
||||
|
||||
val fullName = chatInfo.fullName.trim()
|
||||
if (fullName.isNotEmpty() && fullName != chatInfo.displayName && fullName != chatInfo.displayName.trim()) {
|
||||
Text(
|
||||
fullName,
|
||||
style = MaterialTheme.typography.h4,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.widthIn(max = 260.dp)
|
||||
)
|
||||
}
|
||||
|
||||
val descr = chatInfo.shortDescr?.trim()
|
||||
if (descr != null && descr != "") {
|
||||
Text(
|
||||
descr,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 4,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
}
|
||||
|
||||
val contextStr = chatContext()
|
||||
if (contextStr != null) {
|
||||
Spacer(modifier = Modifier.height(0.dp))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF, Alignment.CenterHorizontally),
|
||||
) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_info),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.secondary
|
||||
)
|
||||
Text(
|
||||
contextStr,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.align(Alignment.BottomCenter),
|
||||
state = listState.value,
|
||||
@@ -1768,42 +1892,47 @@ fun BoxScope.ChatItemsList(
|
||||
) {
|
||||
val mergedItemsValue = mergedItems.value
|
||||
itemsIndexed(mergedItemsValue.items, key = { _, merged -> keyForItem(merged.newest().item) }) { index, merged ->
|
||||
val isLastItem = index == mergedItemsValue.items.lastIndex
|
||||
val last = if (isLastItem) reversedChatItems.value.lastOrNull() else null
|
||||
val listItem = merged.newest()
|
||||
val item = listItem.item
|
||||
val range = if (merged is MergedItem.Grouped) {
|
||||
merged.rangeInReversed.value
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val showAvatar = shouldShowAvatar(item, merged.oldest().nextItem)
|
||||
val isRevealed = remember { derivedStateOf { revealedItems.value.contains(item.id) } }
|
||||
val itemSeparation: ItemSeparation
|
||||
val prevItemSeparationLargeGap: Boolean
|
||||
if (merged is MergedItem.Single || isRevealed.value) {
|
||||
val prev = listItem.prevItem
|
||||
itemSeparation = getItemSeparation(item, prev)
|
||||
val nextForGap = if ((item.mergeCategory != null && item.mergeCategory == prev?.mergeCategory) || isLastItem) null else listItem.nextItem
|
||||
prevItemSeparationLargeGap = if (nextForGap == null) false else getItemSeparationLargeGap(nextForGap, item)
|
||||
} else {
|
||||
itemSeparation = getItemSeparation(item, null)
|
||||
prevItemSeparationLargeGap = false
|
||||
}
|
||||
ChatViewListItem(index == 0, rememberUpdatedState(range), showAvatar, item, itemSeparation, prevItemSeparationLargeGap, isRevealed) {
|
||||
if (merged is MergedItem.Grouped) merged.reveal(it, revealedItems)
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
// no using separate item(){} block in order to have total number of items in LazyColumn match number of merged items
|
||||
DateSeparator(last.meta.itemTs)
|
||||
}
|
||||
if (item.isRcvNew) {
|
||||
val itemIds = when (merged) {
|
||||
is MergedItem.Single -> listOf(merged.item.item.id)
|
||||
is MergedItem.Grouped -> merged.items.map { it.item.id }
|
||||
if (item.content is CIContent.ChatBanner) {
|
||||
ChatBannerView()
|
||||
} else {
|
||||
val isLastItem = index == mergedItemsValue.items.lastIndex
|
||||
val last = if (isLastItem) reversedChatItems.value.lastOrNull() else null
|
||||
val range = if (merged is MergedItem.Grouped) {
|
||||
merged.rangeInReversed.value
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val showAvatar = shouldShowAvatar(item, merged.oldest().nextItem)
|
||||
val isRevealed = remember { derivedStateOf { revealedItems.value.contains(item.id) } }
|
||||
val itemSeparation: ItemSeparation
|
||||
val prevItemSeparationLargeGap: Boolean
|
||||
if (merged is MergedItem.Single || isRevealed.value) {
|
||||
val prev = listItem.prevItem
|
||||
itemSeparation = getItemSeparation(item, prev)
|
||||
val nextForGap = if ((item.mergeCategory != null && item.mergeCategory == prev?.mergeCategory) || isLastItem) null else listItem.nextItem
|
||||
prevItemSeparationLargeGap = if (nextForGap == null) false else getItemSeparationLargeGap(nextForGap, item)
|
||||
} else {
|
||||
itemSeparation = getItemSeparation(item, null)
|
||||
prevItemSeparationLargeGap = false
|
||||
}
|
||||
ChatViewListItem(index == 0, rememberUpdatedState(range), showAvatar, item, itemSeparation, prevItemSeparationLargeGap, isRevealed) {
|
||||
if (merged is MergedItem.Grouped) merged.reveal(it, revealedItems)
|
||||
}
|
||||
|
||||
if (last != null) {
|
||||
// no using separate item(){} block in order to have total number of items in LazyColumn match number of merged items
|
||||
DateSeparator(last.meta.itemTs)
|
||||
}
|
||||
if (item.isRcvNew) {
|
||||
val itemIds = when (merged) {
|
||||
is MergedItem.Single -> listOf(merged.item.item.id)
|
||||
is MergedItem.Grouped -> merged.items.map { it.item.id }
|
||||
}
|
||||
MarkItemsReadAfterDelay(keyForItem(item), itemIds, finishedInitialComposition, chatInfo.id, listState, markItemsRead)
|
||||
}
|
||||
MarkItemsReadAfterDelay(keyForItem(item), itemIds, finishedInitialComposition, chatInfo.id, listState, markItemsRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -787,6 +787,7 @@ fun ChatItemView(
|
||||
is CIContent.RcvDirectE2EEInfo -> DirectE2EEInfoText(c.e2eeInfo)
|
||||
is CIContent.SndGroupE2EEInfo -> E2EEInfoNoPQText()
|
||||
is CIContent.RcvGroupE2EEInfo -> E2EEInfoNoPQText()
|
||||
is CIContent.ChatBanner -> Spacer(modifier = Modifier.size(0.dp))
|
||||
is CIContent.InvalidJSON -> {
|
||||
CIInvalidJSONView(c.json)
|
||||
DeleteItemMenu()
|
||||
|
||||
@@ -485,6 +485,16 @@
|
||||
<string name="group_new_support_chat_one">1 chat with a member</string>
|
||||
<string name="group_new_support_chats_short">%d chat(s)</string>
|
||||
|
||||
<!-- ChatBannerView -->
|
||||
<string name="chat_banner_1_time_invitation">1-time invitation</string>
|
||||
<string name="chat_banner_contact_address">Contact address</string>
|
||||
<string name="chat_banner_contact_request">Contact request</string>
|
||||
<string name="chat_banner_contact">Contact</string>
|
||||
<string name="chat_banner_your_group">Your group</string>
|
||||
<string name="chat_banner_group">Group</string>
|
||||
<string name="chat_banner_business">Business</string>
|
||||
<string name="chat_banner_customer">Customer</string>
|
||||
|
||||
<!-- ShareListView.kt -->
|
||||
<string name="share_message">Share message…</string>
|
||||
<string name="share_image">Share media…</string>
|
||||
|
||||
Reference in New Issue
Block a user