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.material.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.ui.*
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.drawWithCache
|
import androidx.compose.ui.draw.drawWithCache
|
||||||
@@ -988,6 +989,8 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
|||||||
|
|
||||||
Spacer(Modifier.size(8.dp))
|
Spacer(Modifier.size(8.dp))
|
||||||
val reversedChatItems by remember { derivedStateOf { chatModel.chatItems.asReversed() } }
|
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 maxHeightRounded = with(LocalDensity.current) { maxHeight.roundToPx() }
|
||||||
val scrollToItem: (Long) -> Unit = { itemId: Long ->
|
val scrollToItem: (Long) -> Unit = { itemId: Long ->
|
||||||
val index = reversedChatItems.indexOfFirst { it.id == itemId }
|
val index = reversedChatItems.indexOfFirst { it.id == itemId }
|
||||||
@@ -1011,8 +1014,8 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
|||||||
VideoPlayerHolder.releaseAll()
|
VideoPlayerHolder.releaseAll()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
LazyColumnWithScrollBar(Modifier.align(Alignment.BottomCenter), state = listState, reverseLayout = true) {
|
@Composable
|
||||||
itemsIndexed(reversedChatItems, key = { _, item -> item.id to item.meta.createdAt.toEpochMilliseconds() }) { i, cItem ->
|
fun ChatViewListItem(i: Int, showAvatar: Boolean, cItem: ChatItem) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
// Makes horizontal and vertical scrolling to coexist nicely.
|
// Makes horizontal and vertical scrolling to coexist nicely.
|
||||||
// With default touchSlop when you scroll LazyColumn, you can unintentionally open reply view
|
// With default touchSlop when you scroll LazyColumn, you can unintentionally open reply view
|
||||||
@@ -1104,7 +1107,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
|||||||
} else {
|
} else {
|
||||||
null to 1
|
null to 1
|
||||||
}
|
}
|
||||||
if (prevItem == null || showMemberImage(member, prevItem) || prevMember != null) {
|
if (showMemberImage(member, prevItem) || showAvatar) {
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 8.dp)
|
.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) {
|
if (cItem.isRcvNew && chatInfo.id == ChatModel.chatId.value) {
|
||||||
LaunchedEffect(cItem.id) {
|
LaunchedEffect(cItem.id) {
|
||||||
scope.launch {
|
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
|
### 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
|
### Messages to manage groups and add members
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user