mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16edc24c16 | |||
| 1cb3c25478 |
+60
@@ -0,0 +1,60 @@
|
||||
package chat.simplex.common.views.chat
|
||||
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import chat.simplex.common.model.*
|
||||
|
||||
data class SectionItems (
|
||||
val mergeCategory: CIMergeCategory?,
|
||||
val items: SnapshotStateList<ChatItem>,
|
||||
val revealed: Boolean,
|
||||
val showAvatar: MutableSet<Long>,
|
||||
)
|
||||
|
||||
fun List<ChatItem>.putIntoGroups(revealedItems: Set<Long>): List<SectionItems> {
|
||||
println("LALAL LENGTH ${size}")
|
||||
val start = System.currentTimeMillis()
|
||||
val groups = ArrayList<SectionItems>()
|
||||
var recent: SectionItems = if (isNotEmpty()) {
|
||||
val first = this[0]
|
||||
SectionItems(
|
||||
mergeCategory = first.mergeCategory,
|
||||
items = SnapshotStateList<ChatItem>().also { it.add(first) },
|
||||
revealed = first.mergeCategory == null || revealedItems.contains(first.id),
|
||||
showAvatar = mutableSetOf<Long>().also { if (first.chatDir is CIDirection.GroupRcv) it.add(first.id) })
|
||||
} else {
|
||||
return emptyList()
|
||||
}
|
||||
groups.add(recent)
|
||||
var prev = this[0]
|
||||
var index = 0
|
||||
while (index < size) {
|
||||
if (index == 0) {
|
||||
index++
|
||||
continue
|
||||
}
|
||||
val item = this[index]
|
||||
val category = item.mergeCategory
|
||||
if (recent.mergeCategory == category) {
|
||||
recent.items.add(item)
|
||||
if (item.chatDir is CIDirection.GroupRcv && prev.chatDir is CIDirection.GroupRcv && item.chatDir.groupMember == (prev.chatDir as CIDirection.GroupRcv).groupMember) {
|
||||
recent.showAvatar.add(item.id)
|
||||
}
|
||||
} else {
|
||||
recent = SectionItems(
|
||||
mergeCategory = item.mergeCategory,
|
||||
items = SnapshotStateList<ChatItem>().also { it.add(item) },
|
||||
revealed = item.mergeCategory == null || revealedItems.contains(item.id),
|
||||
showAvatar = mutableSetOf<Long>().also {
|
||||
if (item.chatDir is CIDirection.GroupRcv && (prev.chatDir !is CIDirection.GroupRcv || (prev.chatDir as CIDirection.GroupRcv).groupMember != item.chatDir.groupMember)) {
|
||||
it.add(item.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
groups.add(recent)
|
||||
}
|
||||
prev = item
|
||||
index++
|
||||
}
|
||||
println("LALAL RES ${System.currentTimeMillis() - start}, groups: ${groups.size}")
|
||||
return groups
|
||||
}
|
||||
+29
-34
@@ -11,6 +11,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
|
||||
import androidx.compose.ui.*
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
@@ -988,6 +989,8 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
val reversedChatItems by remember { derivedStateOf { chatModel.chatItems.asReversed() } }
|
||||
val revealedItems = rememberSaveable { mutableStateOf(setOf<Long>()) }
|
||||
val groups by remember { derivedStateOf { (reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems + reversedChatItems).putIntoGroups(revealedItems.value) } }
|
||||
val maxHeightRounded = with(LocalDensity.current) { maxHeight.roundToPx() }
|
||||
val scrollToItem: (Long) -> Unit = { itemId: Long ->
|
||||
val index = reversedChatItems.indexOfFirst { it.id == itemId }
|
||||
@@ -1011,8 +1014,8 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
VideoPlayerHolder.releaseAll()
|
||||
}
|
||||
)
|
||||
LazyColumnWithScrollBar(Modifier.align(Alignment.BottomCenter), state = listState, reverseLayout = true) {
|
||||
itemsIndexed(reversedChatItems, key = { _, item -> item.id to item.meta.createdAt.toEpochMilliseconds() }) { i, cItem ->
|
||||
@Composable
|
||||
fun ChatViewListItem(i: Int, showAvatar: Boolean, cItem: ChatItem) {
|
||||
CompositionLocalProvider(
|
||||
// Makes horizontal and vertical scrolling to coexist nicely.
|
||||
// With default touchSlop when you scroll LazyColumn, you can unintentionally open reply view
|
||||
@@ -1104,7 +1107,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
} else {
|
||||
null to 1
|
||||
}
|
||||
if (prevItem == null || showMemberImage(member, prevItem) || prevMember != null) {
|
||||
if (showMemberImage(member, prevItem) || showAvatar) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(top = 8.dp)
|
||||
@@ -1225,37 +1228,6 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val (currIndex, nextItem) = chatModel.getNextChatItem(cItem)
|
||||
val ciCategory = cItem.mergeCategory
|
||||
if (ciCategory != null && ciCategory == nextItem?.mergeCategory) {
|
||||
// memberConnected events and deleted items are aggregated at the last chat item in a row, see ChatItemView
|
||||
} else {
|
||||
val (prevHidden, prevItem) = chatModel.getPrevShownChatItem(currIndex, ciCategory)
|
||||
|
||||
val itemSeparation = getItemSeparation(cItem, nextItem)
|
||||
val previousItemSeparation = if (prevItem != null) getItemSeparation(prevItem, cItem) else null
|
||||
|
||||
if (itemSeparation.date != null) {
|
||||
DateSeparator(itemSeparation.date)
|
||||
}
|
||||
|
||||
val range = chatViewItemsRange(currIndex, prevHidden)
|
||||
if (revealed.value && range != null) {
|
||||
reversedChatItems.subList(range.first, range.last + 1).forEachIndexed { index, ci ->
|
||||
val prev = if (index + range.first == prevHidden) prevItem else reversedChatItems[index + range.first + 1]
|
||||
ChatItemView(ci, null, prev, itemSeparation, previousItemSeparation)
|
||||
}
|
||||
} else {
|
||||
ChatItemView(cItem, range, prevItem, itemSeparation, previousItemSeparation)
|
||||
}
|
||||
|
||||
if (i == reversedChatItems.lastIndex) {
|
||||
DateSeparator(cItem.meta.itemTs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (cItem.isRcvNew && chatInfo.id == ChatModel.chatId.value) {
|
||||
LaunchedEffect(cItem.id) {
|
||||
scope.launch {
|
||||
@@ -1264,6 +1236,29 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
}
|
||||
}
|
||||
}
|
||||
val itemSeparation = getItemSeparation(cItem, null)
|
||||
ChatItemView(cItem, null, null, itemSeparation, null)
|
||||
}
|
||||
}
|
||||
LazyColumnWithScrollBar(Modifier.align(Alignment.BottomCenter), state = listState, reverseLayout = true) {
|
||||
for (group in groups) {
|
||||
if (group.revealed) {
|
||||
itemsIndexed(group.items, key = { _, item -> (item.id to item.meta.createdAt.toEpochMilliseconds()).toString() }) { i, cItem ->
|
||||
// index here is just temporary, should be removed at all or put in the section items
|
||||
ChatViewListItem(reversedChatItems.indexOf(cItem), showAvatar = group.showAvatar.contains(cItem.id), cItem)
|
||||
}
|
||||
} else {
|
||||
val item = group.items.last()
|
||||
item(key = { (item.id to item.meta.createdAt.toEpochMilliseconds()).toString() }) {
|
||||
// here you make one collapsed item from multiple items (should be already in section items)
|
||||
ChatViewListItem(reversedChatItems.indexOf(item), showAvatar = group.showAvatar.contains(item.id), item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reversedChatItems.isNotEmpty()) {
|
||||
item {
|
||||
DateSeparator(reversedChatItems.last().meta.itemTs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ While introduced members establish connection inside group, inviting member forw
|
||||
|
||||
### Member roles
|
||||
|
||||
Currently members can have one of three roles - `owner`, `admin`, `member` and `observer`. The user that created the group is self-assigned owner role, the new members are assigned role by the member who adds them - only `owner` and `admin` members can add new members; only `owner` members can add members with `owner` role. `Observer` members only receive messages and aren't allowed to send messages.
|
||||
Currently members can have one of four roles - `owner`, `admin`, `member` and `observer`. The user that created the group is self-assigned owner role, the new members are assigned role by the member who adds them - only `owner` and `admin` members can add new members; only `owner` members can add members with `owner` role. `Observer` members only receive messages and aren't allowed to send messages.
|
||||
|
||||
### Messages to manage groups and add members
|
||||
|
||||
|
||||
Reference in New Issue
Block a user