mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-17 01:35:08 +00:00
kotlin: refactor chat contexts 2 (null secondary context, pass context instead of content tag, straighten chat state code) (#5830)
This commit is contained in:
-3
@@ -80,9 +80,6 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
|
||||
chatModel.chatId.value = null
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ChatList, nothing to do. Maybe to show other view except ChatList
|
||||
|
||||
@@ -339,7 +339,7 @@ fun AndroidScreen(userPickerState: MutableStateFlow<AnimatedViewState>) {
|
||||
.graphicsLayer { translationX = maxWidth.toPx() - minOf(offset.value.dp, maxWidth).toPx() }
|
||||
) Box2@{
|
||||
currentChatId.value?.let {
|
||||
ChatView(currentChatId, contentTag = null, onComposed = onComposed)
|
||||
ChatView(chatsCtx = chatModel.chatsContext, currentChatId, onComposed = onComposed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,7 +393,7 @@ fun CenterPartOfScreen() {
|
||||
ModalManager.center.showInView()
|
||||
}
|
||||
}
|
||||
else -> ChatView(currentChatId, contentTag = null) {}
|
||||
else -> ChatView(chatsCtx = chatModel.chatsContext, currentChatId) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-53
@@ -67,7 +67,7 @@ object ChatModel {
|
||||
val chatId = mutableStateOf<String?>(null)
|
||||
val openAroundItemId: MutableState<Long?> = mutableStateOf(null)
|
||||
val chatsContext = ChatsContext(null)
|
||||
val secondaryChatsContext = ChatsContext(MsgContentTag.Report)
|
||||
val secondaryChatsContext = mutableStateOf<ChatsContext?>(null)
|
||||
// declaration of chatsContext should be before any other variable that is taken from ChatsContext class and used in the model, otherwise, strange crash with NullPointerException for "this" parameter in random functions
|
||||
val chats: State<List<Chat>> = chatsContext.chats
|
||||
// rhId, chatId
|
||||
@@ -294,16 +294,15 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
class ChatsContext(private val contentTag: MsgContentTag?) {
|
||||
class ChatsContext(val contentTag: MsgContentTag?) {
|
||||
val chats = mutableStateOf(SnapshotStateList<Chat>())
|
||||
/** if you modify the items by adding/removing them, use helpers methods like [addAndNotify], [removeLastAndNotify], [removeAllAndNotify], [clearAndNotify] and so on.
|
||||
/** if you modify the items by adding/removing them, use helpers methods like [addToChatItems], [removeLastChatItems], [removeAllAndNotify], [clearAndNotify] and so on.
|
||||
* If some helper is missing, create it. Notify is needed to track state of items that we added manually (not via api call). See [apiLoadMessages].
|
||||
* If you use api call to get the items, use just [add] instead of [addAndNotify].
|
||||
* If you use api call to get the items, use just [add] instead of [addToChatItems].
|
||||
* Never modify underlying list directly because it produces unexpected results in ChatView's LazyColumn (setting by index is ok) */
|
||||
val chatItems = mutableStateOf(SnapshotStateList<ChatItem>())
|
||||
val chatItemStatuses = mutableMapOf<Long, CIStatus>()
|
||||
// set listener here that will be notified on every add/delete of a chat item
|
||||
var chatItemsChangesListener: ChatItemsChangesListener? = null
|
||||
val chatState = ActiveChatState()
|
||||
|
||||
fun hasChat(rhId: Long?, id: String): Boolean = chats.value.firstOrNull { it.id == id && it.remoteHostId == rhId } != null
|
||||
@@ -395,6 +394,26 @@ object ChatModel {
|
||||
addChat(chat)
|
||||
}
|
||||
}
|
||||
|
||||
fun addToChatItems(index: Int, elem: ChatItem) {
|
||||
chatItems.value = SnapshotStateList<ChatItem>().apply { addAll(chatItems.value); add(index, elem); chatState.itemAdded(elem.id to elem.isRcvNew) }
|
||||
}
|
||||
|
||||
fun addToChatItems(elem: ChatItem) {
|
||||
chatItems.value = SnapshotStateList<ChatItem>().apply { addAll(chatItems.value); add(elem); chatState.itemAdded(elem.id to elem.isRcvNew) }
|
||||
}
|
||||
|
||||
fun removeLastChatItems() {
|
||||
val removed: Triple<Long, Int, Boolean>
|
||||
chatItems.value = SnapshotStateList<ChatItem>().apply {
|
||||
addAll(chatItems.value)
|
||||
val remIndex = lastIndex
|
||||
val rem = removeLast()
|
||||
removed = Triple(rem.id, remIndex, rem.isRcvNew)
|
||||
}
|
||||
chatState.itemsRemoved(listOf(removed), chatItems.value)
|
||||
}
|
||||
|
||||
suspend fun addChatItem(rhId: Long?, cInfo: ChatInfo, cItem: ChatItem) {
|
||||
// mark chat non deleted
|
||||
if (cInfo is ChatInfo.Direct && cInfo.chatDeleted) {
|
||||
@@ -448,9 +467,9 @@ object ChatModel {
|
||||
// Prevent situation when chat item already in the list received from backend
|
||||
if (chatItems.value.none { it.id == cItem.id }) {
|
||||
if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
chatItems.addAndNotify(kotlin.math.max(0, chatItems.value.lastIndex), cItem, contentTag)
|
||||
addToChatItems(kotlin.math.max(0, chatItems.value.lastIndex), cItem)
|
||||
} else {
|
||||
chatItems.addAndNotify(cItem, contentTag)
|
||||
addToChatItems(cItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,7 +514,7 @@ object ChatModel {
|
||||
} else {
|
||||
cItem
|
||||
}
|
||||
chatItems.addAndNotify(ci, contentTag)
|
||||
addToChatItems(ci)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
@@ -602,9 +621,10 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
val popChatCollector = PopChatCollector(contentTag)
|
||||
val popChatCollector = PopChatCollector(this)
|
||||
|
||||
class PopChatCollector(contentTag: MsgContentTag?) {
|
||||
// TODO [contexts] no reason for this to be nested?
|
||||
class PopChatCollector(chatsCtx: ChatsContext) {
|
||||
private val subject = MutableSharedFlow<Unit>()
|
||||
private var remoteHostId: Long? = null
|
||||
private val chatsToPop = mutableMapOf<ChatId, Instant>()
|
||||
@@ -615,7 +635,6 @@ object ChatModel {
|
||||
.throttleLatest(2000)
|
||||
.collect {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = if (contentTag == null) chatsContext else secondaryChatsContext
|
||||
chatsCtx.chats.replaceAll(popCollectedChats())
|
||||
}
|
||||
}
|
||||
@@ -704,7 +723,7 @@ object ChatModel {
|
||||
}
|
||||
i--
|
||||
}
|
||||
chatItemsChangesListener?.read(if (itemIds != null) markedReadIds else null, items)
|
||||
chatState.itemsRead(if (itemIds != null) markedReadIds else null, items)
|
||||
}
|
||||
return markedRead to mentionsMarkedRead
|
||||
}
|
||||
@@ -917,7 +936,7 @@ object ChatModel {
|
||||
suspend fun addLiveDummy(chatInfo: ChatInfo): ChatItem {
|
||||
val cItem = ChatItem.liveDummy(chatInfo is ChatInfo.Direct)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsContext.chatItems.addAndNotify(cItem, contentTag = null)
|
||||
chatsContext.addToChatItems(cItem)
|
||||
}
|
||||
return cItem
|
||||
}
|
||||
@@ -926,7 +945,7 @@ object ChatModel {
|
||||
if (chatsContext.chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
withApi {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsContext.chatItems.removeLastAndNotify(contentTag = null)
|
||||
chatsContext.removeLastChatItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1000,8 +1019,6 @@ object ChatModel {
|
||||
withApi {
|
||||
withContext(Dispatchers.Main) {
|
||||
showingInvitation.value = null
|
||||
// TODO [contexts] - why does clearAndNotify operates with listeners for both contexts?
|
||||
// TODO - should it be called for both contexts here instead?
|
||||
chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatId.value = withId
|
||||
}
|
||||
@@ -1015,7 +1032,6 @@ object ChatModel {
|
||||
if (id == showingInvitation.value?.connId) {
|
||||
withContext(Dispatchers.Main) {
|
||||
showingInvitation.value = null
|
||||
// TODO [contexts] see replaceConnReqView
|
||||
chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatId.value = null
|
||||
}
|
||||
@@ -1067,15 +1083,6 @@ object ChatModel {
|
||||
fun connectedToRemote(): Boolean = currentRemoteHost.value != null || remoteCtrlSession.value?.active == true
|
||||
}
|
||||
|
||||
interface ChatItemsChangesListener {
|
||||
// pass null itemIds if the whole chat now read
|
||||
fun read(itemIds: Set<Long>?, newItems: List<ChatItem>)
|
||||
fun added(item: Pair<Long, Boolean>, index: Int)
|
||||
// itemId, index in old chatModel.chatItems (before the update), isRcvNew (is item unread or not)
|
||||
fun removed(itemIds: List<Triple<Long, Int, Boolean>>, newItems: List<ChatItem>)
|
||||
fun cleared()
|
||||
}
|
||||
|
||||
data class ShowingInvitation(
|
||||
val connId: String,
|
||||
val connLink: CreatedConnLink,
|
||||
@@ -2697,11 +2704,6 @@ fun MutableState<SnapshotStateList<Chat>>.add(index: Int, elem: Chat) {
|
||||
value = SnapshotStateList<Chat>().apply { addAll(value); add(index, elem) }
|
||||
}
|
||||
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.addAndNotify(index: Int, elem: ChatItem, contentTag: MsgContentTag?) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(index, elem); chatsCtx.chatItemsChangesListener?.added(elem.id to elem.isRcvNew, index) }
|
||||
}
|
||||
|
||||
fun MutableState<SnapshotStateList<Chat>>.add(elem: Chat) {
|
||||
value = SnapshotStateList<Chat>().apply { addAll(value); add(elem) }
|
||||
}
|
||||
@@ -2709,12 +2711,6 @@ fun MutableState<SnapshotStateList<Chat>>.add(elem: Chat) {
|
||||
// For some reason, Kotlin version crashes if the list is empty
|
||||
fun <T> MutableList<T>.removeAll(predicate: (T) -> Boolean): Boolean = if (isEmpty()) false else remAll(predicate)
|
||||
|
||||
// Adds item to chatItems and notifies a listener about newly added item
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.addAndNotify(elem: ChatItem, contentTag: MsgContentTag?) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(elem); chatsCtx.chatItemsChangesListener?.added(elem.id to elem.isRcvNew, lastIndex) }
|
||||
}
|
||||
|
||||
fun <T> MutableState<SnapshotStateList<T>>.addAll(index: Int, elems: List<T>) {
|
||||
value = SnapshotStateList<T>().apply { addAll(value); addAll(index, elems) }
|
||||
}
|
||||
@@ -2727,6 +2723,7 @@ fun MutableState<SnapshotStateList<Chat>>.removeAll(block: (Chat) -> Boolean) {
|
||||
value = SnapshotStateList<Chat>().apply { addAll(value); removeAll(block) }
|
||||
}
|
||||
|
||||
// TODO [contexts] operates with both contexts?
|
||||
// Removes item(s) from chatItems and notifies a listener about removed item(s)
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.removeAllAndNotify(block: (ChatItem) -> Boolean) {
|
||||
val toRemove = ArrayList<Triple<Long, Int, Boolean>>()
|
||||
@@ -2741,8 +2738,8 @@ fun MutableState<SnapshotStateList<ChatItem>>.removeAllAndNotify(block: (ChatIte
|
||||
}
|
||||
}
|
||||
if (toRemove.isNotEmpty()) {
|
||||
chatModel.chatsContext.chatItemsChangesListener?.removed(toRemove, value)
|
||||
chatModel.secondaryChatsContext.chatItemsChangesListener?.removed(toRemove, value)
|
||||
chatModel.chatsContext.chatState.itemsRemoved(toRemove, value)
|
||||
chatModel.secondaryChatsContext.value?.chatState?.itemsRemoved(toRemove, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2754,18 +2751,6 @@ fun MutableState<SnapshotStateList<Chat>>.removeAt(index: Int): Chat {
|
||||
return res
|
||||
}
|
||||
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.removeLastAndNotify(contentTag: MsgContentTag?) {
|
||||
val removed: Triple<Long, Int, Boolean>
|
||||
value = SnapshotStateList<ChatItem>().apply {
|
||||
addAll(value)
|
||||
val remIndex = lastIndex
|
||||
val rem = removeLast()
|
||||
removed = Triple(rem.id, remIndex, rem.isRcvNew)
|
||||
}
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
chatsCtx.chatItemsChangesListener?.removed(listOf(removed), value)
|
||||
}
|
||||
|
||||
fun <T> MutableState<SnapshotStateList<T>>.replaceAll(elems: List<T>) {
|
||||
value = SnapshotStateList<T>().apply { addAll(elems) }
|
||||
}
|
||||
@@ -2774,11 +2759,12 @@ fun MutableState<SnapshotStateList<Chat>>.clear() {
|
||||
value = SnapshotStateList()
|
||||
}
|
||||
|
||||
// TODO [contexts] operates with both contexts?
|
||||
// Removes all chatItems and notifies a listener about it
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.clearAndNotify() {
|
||||
value = SnapshotStateList()
|
||||
chatModel.chatsContext.chatItemsChangesListener?.cleared()
|
||||
chatModel.secondaryChatsContext.chatItemsChangesListener?.cleared()
|
||||
chatModel.chatsContext.chatState.clear()
|
||||
chatModel.secondaryChatsContext.value?.chatState?.clear()
|
||||
}
|
||||
|
||||
fun <T> State<SnapshotStateList<T>>.asReversed(): MutableList<T> = value.asReversed()
|
||||
|
||||
+32
-60
@@ -1553,7 +1553,7 @@ object ChatController {
|
||||
chatModel.chatsContext.clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.secondaryChatsContext.clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
chatModel.secondaryChatsContext.value?.clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
}
|
||||
ntfManager.cancelNotificationsForChat(chat.chatInfo.id)
|
||||
close?.invoke()
|
||||
@@ -2493,9 +2493,7 @@ object ChatController {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2550,10 +2548,8 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.addChatItem(rhId, cInfo, cItem)
|
||||
}
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.value?.addChatItem(rhId, cInfo, cItem)
|
||||
}
|
||||
}
|
||||
} else if (cItem.isRcvNew && cInfo.ntfsEnabled(cItem)) {
|
||||
@@ -2583,10 +2579,8 @@ object ChatController {
|
||||
chatModel.chatsContext.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
|
||||
}
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.value?.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2599,10 +2593,8 @@ object ChatController {
|
||||
chatModel.chatsContext.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (r.reaction.chatReaction.chatItem.isReport) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
|
||||
}
|
||||
if (r.reaction.chatReaction.chatItem.isReport) {
|
||||
chatModel.secondaryChatsContext.value?.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2646,13 +2638,11 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
if (toChatItem == null) {
|
||||
chatModel.secondaryChatsContext.removeChatItem(rhId, cInfo, cItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(rhId, cInfo, toChatItem.chatItem)
|
||||
}
|
||||
if (cItem.isReport) {
|
||||
if (toChatItem == null) {
|
||||
chatModel.secondaryChatsContext.value?.removeChatItem(rhId, cInfo, cItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(rhId, cInfo, toChatItem.chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2722,10 +2712,8 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (r.withMessages) {
|
||||
chatModel.secondaryChatsContext.removeMemberItems(rhId, r.groupInfo.membership, byMember = r.member, r.groupInfo)
|
||||
}
|
||||
if (r.withMessages) {
|
||||
chatModel.secondaryChatsContext.value?.removeMemberItems(rhId, r.groupInfo.membership, byMember = r.member, r.groupInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2738,11 +2726,9 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
if (r.withMessages) {
|
||||
chatModel.secondaryChatsContext.removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
if (r.withMessages) {
|
||||
chatModel.secondaryChatsContext.value?.removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2752,9 +2738,7 @@ object ChatController {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
is CR.MemberRole ->
|
||||
@@ -2763,9 +2747,7 @@ object ChatController {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
is CR.MembersRoleUser ->
|
||||
@@ -2776,10 +2758,8 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
r.members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, member)
|
||||
}
|
||||
r.members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2789,9 +2769,7 @@ object ChatController {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
is CR.GroupDeleted -> // TODO update user member
|
||||
@@ -3167,10 +3145,8 @@ object ChatController {
|
||||
val cItem = aChatItem.chatItem
|
||||
withContext(Dispatchers.Main) { chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem) }
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(rh, cInfo, cItem)
|
||||
}
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(rh, cInfo, cItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3212,8 +3188,8 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
val chatsCtx = chatModel.secondaryChatsContext
|
||||
val chatsCtx = chatModel.secondaryChatsContext.value
|
||||
if (chatsCtx != null) {
|
||||
r.chatItemIDs.forEach { itemId ->
|
||||
val cItem = chatsCtx.chatItems.value.lastOrNull { it.id == itemId } ?: return@forEach
|
||||
if (chatModel.chatId.value != null) {
|
||||
@@ -3240,10 +3216,8 @@ object ChatController {
|
||||
} else {
|
||||
val createdChat = withContext(Dispatchers.Main) { chatModel.chatsContext.upsertChatItem(rh, cInfo, cItem) }
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.content.msgContent is MsgContent.MCReport) {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(rh, cInfo, cItem)
|
||||
}
|
||||
if (cItem.content.msgContent is MsgContent.MCReport) {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(rh, cInfo, cItem)
|
||||
}
|
||||
}
|
||||
if (createdChat) {
|
||||
@@ -3296,11 +3270,9 @@ object ChatController {
|
||||
chatModel.chatsContext.popChatCollector.clear()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.chatItems.clearAndNotify()
|
||||
chatModel.secondaryChatsContext.chats.clear()
|
||||
chatModel.secondaryChatsContext.popChatCollector.clear()
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.chatItems?.clearAndNotify()
|
||||
chatModel.secondaryChatsContext.value?.chats?.clear()
|
||||
chatModel.secondaryChatsContext.value?.popChatCollector?.clear()
|
||||
}
|
||||
}
|
||||
val statuses = apiGetNetworkStatuses(rhId)
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ abstract class NtfManager {
|
||||
}
|
||||
val cInfo = chatModel.getChat(chatId)?.chatInfo
|
||||
chatModel.clearOverlays.value = true
|
||||
if (cInfo != null && (cInfo is ChatInfo.Direct || cInfo is ChatInfo.Group)) openChat(null, cInfo)
|
||||
if (cInfo != null && (cInfo is ChatInfo.Direct || cInfo is ChatInfo.Group)) openChat(secondaryChatsCtx = null, rhId = null, cInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-6
@@ -55,6 +55,7 @@ import java.io.File
|
||||
|
||||
@Composable
|
||||
fun ChatInfoView(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chatModel: ChatModel,
|
||||
contact: Contact,
|
||||
connectionStats: ConnectionStats?,
|
||||
@@ -97,7 +98,7 @@ fun ChatInfoView(
|
||||
val previousChatTTL = chatItemTTL.value
|
||||
chatItemTTL.value = it
|
||||
|
||||
setChatTTLAlert(chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
|
||||
setChatTTLAlert(chatsCtx, chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
|
||||
},
|
||||
connStats = connStats,
|
||||
contactNetworkStatus.value,
|
||||
@@ -1332,6 +1333,7 @@ fun queueInfoText(info: Pair<RcvMsgInfo?, ServerQueueInfo>): String {
|
||||
}
|
||||
|
||||
fun setChatTTLAlert(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chatInfo: ChatInfo,
|
||||
selectedChatTTL: MutableState<ChatItemTTL?>,
|
||||
@@ -1351,7 +1353,7 @@ fun setChatTTLAlert(
|
||||
} else MR.strings.enable_automatic_deletion_question),
|
||||
text = generalGetString(if (newTTLToUse.neverExpires) MR.strings.disable_automatic_deletion_message else MR.strings.change_automatic_chat_deletion_message),
|
||||
confirmText = generalGetString(if (newTTLToUse.neverExpires) MR.strings.disable_automatic_deletion else MR.strings.delete_messages),
|
||||
onConfirm = { setChatTTL(rhId, chatInfo, selectedChatTTL, progressIndicator, previousChatTTL) },
|
||||
onConfirm = { setChatTTL(chatsCtx, rhId, chatInfo, selectedChatTTL, progressIndicator, previousChatTTL) },
|
||||
onDismiss = { selectedChatTTL.value = previousChatTTL },
|
||||
onDismissRequest = { selectedChatTTL.value = previousChatTTL },
|
||||
destructive = true,
|
||||
@@ -1359,6 +1361,7 @@ fun setChatTTLAlert(
|
||||
}
|
||||
|
||||
private fun setChatTTL(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chatInfo: ChatInfo,
|
||||
chatTTL: MutableState<ChatItemTTL?>,
|
||||
@@ -1369,16 +1372,16 @@ private fun setChatTTL(
|
||||
withBGApi {
|
||||
try {
|
||||
chatModel.controller.setChatTTL(rhId, chatInfo.chatType, chatInfo.apiId, chatTTL.value)
|
||||
afterSetChatTTL(rhId, chatInfo, progressIndicator)
|
||||
afterSetChatTTL(chatsCtx, rhId, chatInfo, progressIndicator)
|
||||
} catch (e: Exception) {
|
||||
chatTTL.value = previousChatTTL
|
||||
afterSetChatTTL(rhId, chatInfo, progressIndicator)
|
||||
afterSetChatTTL(chatsCtx, rhId, chatInfo, progressIndicator)
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_changing_message_deletion), e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun afterSetChatTTL(rhId: Long?, chatInfo: ChatInfo, progressIndicator: MutableState<Boolean>) {
|
||||
private suspend fun afterSetChatTTL(chatsCtx: ChatModel.ChatsContext, rhId: Long?, chatInfo: ChatInfo, progressIndicator: MutableState<Boolean>) {
|
||||
try {
|
||||
val pagination = ChatPagination.Initial(ChatPagination.INITIAL_COUNT)
|
||||
val (chat, navInfo) = controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, null, pagination) ?: return
|
||||
@@ -1393,9 +1396,9 @@ private suspend fun afterSetChatTTL(rhId: Long?, chatInfo: ChatInfo, progressInd
|
||||
}
|
||||
if (chat.remoteHostId != chatModel.remoteHostId() || chat.id != chatModel.chatId.value) return
|
||||
processLoadedChat(
|
||||
chatsCtx,
|
||||
chat,
|
||||
navInfo,
|
||||
contentTag = null,
|
||||
pagination = pagination,
|
||||
openAroundItemId = null
|
||||
)
|
||||
|
||||
+1
-1
@@ -209,7 +209,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
SectionItemView(
|
||||
click = {
|
||||
withBGApi {
|
||||
openChat(chatRh, forwardedFromItem.chatInfo)
|
||||
openChat(secondaryChatsCtx = null, chatRh, forwardedFromItem.chatInfo)
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
},
|
||||
|
||||
+8
-9
@@ -11,43 +11,42 @@ import kotlin.math.min
|
||||
const val TRIM_KEEP_COUNT = 200
|
||||
|
||||
suspend fun apiLoadSingleMessage(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chatType: ChatType,
|
||||
apiId: Long,
|
||||
itemId: Long,
|
||||
contentTag: MsgContentTag?,
|
||||
itemId: Long
|
||||
): ChatItem? = coroutineScope {
|
||||
val (chat, _) = chatModel.controller.apiGetChat(rhId, chatType, apiId, contentTag, ChatPagination.Around(itemId, 0), "") ?: return@coroutineScope null
|
||||
val (chat, _) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.contentTag, ChatPagination.Around(itemId, 0), "") ?: return@coroutineScope null
|
||||
chat.chatItems.firstOrNull()
|
||||
}
|
||||
|
||||
suspend fun apiLoadMessages(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chatType: ChatType,
|
||||
apiId: Long,
|
||||
contentTag: MsgContentTag?,
|
||||
pagination: ChatPagination,
|
||||
search: String = "",
|
||||
openAroundItemId: Long? = null,
|
||||
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 }
|
||||
) = coroutineScope {
|
||||
val (chat, navInfo) = chatModel.controller.apiGetChat(rhId, chatType, apiId, contentTag, pagination, search) ?: return@coroutineScope
|
||||
val (chat, navInfo) = chatModel.controller.apiGetChat(rhId, chatType, apiId, chatsCtx.contentTag, pagination, search) ?: return@coroutineScope
|
||||
// For .initial allow the chatItems to be empty as well as chatModel.chatId to not match this chat because these values become set after .initial finishes
|
||||
/** When [openAroundItemId] is provided, chatId can be different too */
|
||||
if (((chatModel.chatId.value != chat.id || chat.chatItems.isEmpty()) && pagination !is ChatPagination.Initial && pagination !is ChatPagination.Last && openAroundItemId == null)
|
||||
|| !isActive) return@coroutineScope
|
||||
processLoadedChat(chat, navInfo, contentTag, pagination, openAroundItemId, visibleItemIndexesNonReversed)
|
||||
processLoadedChat(chatsCtx, chat, navInfo, pagination, openAroundItemId, visibleItemIndexesNonReversed)
|
||||
}
|
||||
|
||||
suspend fun processLoadedChat(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chat: Chat,
|
||||
navInfo: NavigationInfo,
|
||||
contentTag: MsgContentTag?,
|
||||
pagination: ChatPagination,
|
||||
openAroundItemId: Long?,
|
||||
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 }
|
||||
) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val chatState = chatsCtx.chatState
|
||||
val (splits, unreadAfterItemId, totalAfter, unreadTotal, unreadAfter, unreadAfterNewestLoaded) = chatState
|
||||
val oldItems = chatsCtx.chatItems.value
|
||||
@@ -55,7 +54,7 @@ suspend fun processLoadedChat(
|
||||
when (pagination) {
|
||||
is ChatPagination.Initial -> {
|
||||
val newSplits = if (chat.chatItems.isNotEmpty() && navInfo.afterTotal > 0) listOf(chat.chatItems.last().id) else emptyList()
|
||||
if (contentTag == null) {
|
||||
if (chatsCtx.contentTag == null) {
|
||||
// update main chats, not content tagged
|
||||
withContext(Dispatchers.Main) {
|
||||
val oldChat = chatModel.chatsContext.getChat(chat.id)
|
||||
|
||||
+20
-23
@@ -237,24 +237,8 @@ data class ActiveChatState (
|
||||
unreadAfter.value = 0
|
||||
unreadAfterNewestLoaded.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
fun visibleItemIndexesNonReversed(mergedItems: State<MergedItems>, reversedItemsSize: Int, listState: LazyListState): IntRange {
|
||||
val zero = 0 .. 0
|
||||
if (listState.layoutInfo.totalItemsCount == 0) return zero
|
||||
val newest = mergedItems.value.items.getOrNull(listState.firstVisibleItemIndex)?.startIndexInReversedItems
|
||||
val oldest = mergedItems.value.items.getOrNull(listState.layoutInfo.visibleItemsInfo.last().index)?.lastIndexInReversed()
|
||||
if (newest == null || oldest == null) return zero
|
||||
val range = reversedItemsSize - oldest .. reversedItemsSize - newest
|
||||
if (range.first < 0 || range.last < 0) return zero
|
||||
|
||||
// visible items mapped to their underlying data structure which is chatModel.chatItems
|
||||
return range
|
||||
}
|
||||
|
||||
fun recalculateChatStatePositions(chatState: ActiveChatState) = object: ChatItemsChangesListener {
|
||||
override fun read(itemIds: Set<Long>?, newItems: List<ChatItem>) {
|
||||
val (_, unreadAfterItemId, _, unreadTotal, unreadAfter) = chatState
|
||||
fun itemsRead(itemIds: Set<Long>?, newItems: List<ChatItem>) {
|
||||
if (itemIds == null) {
|
||||
// special case when the whole chat became read
|
||||
unreadTotal.value = 0
|
||||
@@ -287,14 +271,15 @@ fun recalculateChatStatePositions(chatState: ActiveChatState) = object: ChatItem
|
||||
unreadTotal.value = newUnreadTotal
|
||||
unreadAfter.value = newUnreadAfter
|
||||
}
|
||||
override fun added(item: Pair<Long, Boolean>, index: Int) {
|
||||
|
||||
fun itemAdded(item: Pair<Long, Boolean>) {
|
||||
if (item.second) {
|
||||
chatState.unreadAfter.value++
|
||||
chatState.unreadTotal.value++
|
||||
unreadAfter.value++
|
||||
unreadTotal.value++
|
||||
}
|
||||
}
|
||||
override fun removed(itemIds: List<Triple<Long, Int, Boolean>>, newItems: List<ChatItem>) {
|
||||
val (splits, unreadAfterItemId, totalAfter, unreadTotal, unreadAfter) = chatState
|
||||
|
||||
fun itemsRemoved(itemIds: List<Triple<Long, Int, Boolean>>, newItems: List<ChatItem>) {
|
||||
val newSplits = ArrayList<Long>()
|
||||
for (split in splits.value) {
|
||||
val index = itemIds.indexOfFirst { it.first == split }
|
||||
@@ -343,7 +328,19 @@ fun recalculateChatStatePositions(chatState: ActiveChatState) = object: ChatItem
|
||||
totalAfter.value -= itemIds.size
|
||||
}
|
||||
}
|
||||
override fun cleared() { chatState.clear() }
|
||||
}
|
||||
|
||||
fun visibleItemIndexesNonReversed(mergedItems: State<MergedItems>, reversedItemsSize: Int, listState: LazyListState): IntRange {
|
||||
val zero = 0 .. 0
|
||||
if (listState.layoutInfo.totalItemsCount == 0) return zero
|
||||
val newest = mergedItems.value.items.getOrNull(listState.firstVisibleItemIndex)?.startIndexInReversedItems
|
||||
val oldest = mergedItems.value.items.getOrNull(listState.layoutInfo.visibleItemsInfo.last().index)?.lastIndexInReversed()
|
||||
if (newest == null || oldest == null) return zero
|
||||
val range = reversedItemsSize - oldest .. reversedItemsSize - newest
|
||||
if (range.first < 0 || range.last < 0) return zero
|
||||
|
||||
// visible items mapped to their underlying data structure which is chatModel.chatItems
|
||||
return range
|
||||
}
|
||||
|
||||
/** Helps in debugging */
|
||||
|
||||
+252
-268
@@ -57,8 +57,8 @@ data class ItemSeparation(val timestamp: Boolean, val largeGap: Boolean, val dat
|
||||
// staleChatId means the id that was before chatModel.chatId becomes null. It's needed for Android only to make transition from chat
|
||||
// to chat list smooth. Otherwise, chat view will become blank right before the transition starts
|
||||
fun ChatView(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
staleChatId: State<String?>,
|
||||
contentTag: MsgContentTag?,
|
||||
scrollToItemId: MutableState<Long?> = remember { mutableStateOf(null) },
|
||||
onComposed: suspend (chatId: String) -> Unit
|
||||
) {
|
||||
@@ -99,7 +99,7 @@ fun ChatView(
|
||||
.distinctUntilChanged()
|
||||
.filterNotNull()
|
||||
.collect { chatId ->
|
||||
if (contentTag == null) {
|
||||
if (chatsCtx.contentTag == null) {
|
||||
markUnreadChatAsRead(chatId)
|
||||
}
|
||||
showSearch.value = false
|
||||
@@ -112,7 +112,6 @@ fun ChatView(
|
||||
val chatRh = remoteHostId.value
|
||||
// We need to have real unreadCount value for displaying it inside top right button
|
||||
// Having activeChat reloaded on every change in it is inefficient (UI lags)
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val unreadCount = remember {
|
||||
derivedStateOf {
|
||||
chatsCtx.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatStats?.unreadCount ?: 0
|
||||
@@ -121,7 +120,6 @@ fun ChatView(
|
||||
val clipboard = LocalClipboardManager.current
|
||||
CompositionLocalProvider(
|
||||
LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false),
|
||||
LocalContentTag provides contentTag
|
||||
) {
|
||||
when (chatInfo) {
|
||||
is ChatInfo.Direct, is ChatInfo.Group, is ChatInfo.Local -> {
|
||||
@@ -135,15 +133,16 @@ fun ChatView(
|
||||
val sameText = searchText.value == value
|
||||
// showSearch can be false with empty text when it was closed manually after clicking on message from search to load .around it
|
||||
// (required on Android to have this check to prevent call to search with old text)
|
||||
val emptyAndClosedSearch = searchText.value.isEmpty() && !showSearch.value && contentTag == null
|
||||
val emptyAndClosedSearch = searchText.value.isEmpty() && !showSearch.value && chatsCtx.contentTag == null
|
||||
val c = chatModel.getChat(chatInfo.id)
|
||||
if (sameText || emptyAndClosedSearch || c == null || chatModel.chatId.value != chatInfo.id) return@onSearchValueChanged
|
||||
withBGApi {
|
||||
apiFindMessages(c, value, contentTag)
|
||||
apiFindMessages(chatsCtx, c, value)
|
||||
searchText.value = value
|
||||
}
|
||||
}
|
||||
ChatLayout(
|
||||
chatsCtx = chatsCtx,
|
||||
remoteHostId = remoteHostId,
|
||||
chatInfo = activeChatInfo,
|
||||
unreadCount,
|
||||
@@ -175,7 +174,7 @@ fun ChatView(
|
||||
}
|
||||
} else {
|
||||
SelectedItemsButtonsToolbar(
|
||||
contentTag = contentTag,
|
||||
chatsCtx = chatsCtx,
|
||||
selectedChatItems = selectedChatItems,
|
||||
chatInfo = chatInfo,
|
||||
deleteItems = { canDeleteForAll ->
|
||||
@@ -287,7 +286,7 @@ fun ChatView(
|
||||
code = chatModel.controller.apiGetContactCode(chatRh, chatInfo.apiId)?.second
|
||||
preloadedCode = code
|
||||
}
|
||||
ChatInfoView(chatModel, chatInfo.contact, contactInfo?.first, contactInfo?.second, chatInfo.localAlias, code, close) {
|
||||
ChatInfoView(chatsCtx, chatModel, chatInfo.contact, contactInfo?.first, contactInfo?.second, chatInfo.localAlias, code, close) {
|
||||
showSearch.value = true
|
||||
}
|
||||
} else if (chatInfo is ChatInfo.Group) {
|
||||
@@ -297,7 +296,7 @@ fun ChatView(
|
||||
link = chatModel.controller.apiGetGroupLink(chatRh, chatInfo.groupInfo.groupId)
|
||||
preloadedLink = link
|
||||
}
|
||||
GroupChatInfoView(chatRh, chatInfo.id, link?.first, link?.second, selectedItems, appBar, scrollToItemId, {
|
||||
GroupChatInfoView(chatsCtx, chatRh, chatInfo.id, link?.first, link?.second, selectedItems, appBar, scrollToItemId, {
|
||||
link = it
|
||||
preloadedLink = it
|
||||
}, close, { showSearch.value = true })
|
||||
@@ -344,7 +343,7 @@ fun ChatView(
|
||||
setGroupMembers(chatRh, groupInfo, chatModel)
|
||||
if (!isActive) return@launch
|
||||
|
||||
if (contentTag == null) {
|
||||
if (chatsCtx.contentTag == null) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
@@ -358,12 +357,12 @@ fun ChatView(
|
||||
val c = chatModel.getChat(chatId)
|
||||
if (chatModel.chatId.value != chatId) return@ChatLayout
|
||||
if (c != null) {
|
||||
apiLoadMessages(c.remoteHostId, c.chatInfo.chatType, c.chatInfo.apiId, contentTag, pagination, searchText.value, null, visibleItemIndexes)
|
||||
apiLoadMessages(chatsCtx, c.remoteHostId, c.chatInfo.chatType, c.chatInfo.apiId, pagination, searchText.value, null, visibleItemIndexes)
|
||||
}
|
||||
},
|
||||
deleteMessage = { itemId, mode ->
|
||||
withBGApi {
|
||||
val toDeleteItem = reversedChatItemsStatic(contentTag).lastOrNull { it.id == itemId }
|
||||
val toDeleteItem = reversedChatItemsStatic(chatsCtx).lastOrNull { it.id == itemId }
|
||||
val toModerate = toDeleteItem?.memberToModerate(chatInfo)
|
||||
val groupInfo = toModerate?.first
|
||||
val groupMember = toModerate?.second
|
||||
@@ -400,13 +399,11 @@ fun ChatView(
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (deletedChatItem.isReport) {
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.removeChatItem(chatRh, chatInfo, deletedChatItem)
|
||||
}
|
||||
if (deletedChatItem.isReport) {
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.value?.removeChatItem(chatRh, chatInfo, deletedChatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,10 +521,8 @@ fun ChatView(
|
||||
chatModel.chatsContext.updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.value?.updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,9 +587,7 @@ fun ChatView(
|
||||
)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -610,9 +603,7 @@ fun ChatView(
|
||||
)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.markChatItemsRead(chatRh, chatInfo.id)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.markChatItemsRead(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -676,6 +667,7 @@ fun startChatCall(remoteHostId: Long?, chatInfo: ChatInfo, media: CallMediaType)
|
||||
|
||||
@Composable
|
||||
fun ChatLayout(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
remoteHostId: State<Long?>,
|
||||
chatInfo: State<ChatInfo?>,
|
||||
unreadCount: State<Int>,
|
||||
@@ -753,8 +745,7 @@ fun ChatLayout(
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
val composeViewHeight = remember { mutableStateOf(0.dp) }
|
||||
val contentTag = LocalContentTag.current
|
||||
Box(Modifier.fillMaxSize().chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, contentTag == null)) {
|
||||
Box(Modifier.fillMaxSize().chatViewBackgroundModifier(MaterialTheme.colors, MaterialTheme.wallpaper, LocalAppBarHandler.current?.backgroundGraphicsLayerSize, LocalAppBarHandler.current?.backgroundGraphicsLayer, drawWallpaper = chatsCtx.contentTag == null)) {
|
||||
val remoteHostId = remember { remoteHostId }.value
|
||||
val chatInfo = remember { chatInfo }.value
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
@@ -768,7 +759,7 @@ fun ChatLayout(
|
||||
override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float = 0f
|
||||
}) {
|
||||
ChatItemsList(
|
||||
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
||||
chatsCtx, remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
||||
useLinkPreviews, linkMode, scrollToItemId, selectedChatItems, showMemberInfo, showChatInfo = info, loadMessages, deleteMessage, deleteMessages, archiveReports,
|
||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||
@@ -791,7 +782,7 @@ fun ChatLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (contentTag == MsgContentTag.Report) {
|
||||
if (chatsCtx.contentTag == MsgContentTag.Report) {
|
||||
Column(
|
||||
Modifier
|
||||
.layoutId(CHAT_COMPOSE_LAYOUT_ID)
|
||||
@@ -802,7 +793,7 @@ fun ChatLayout(
|
||||
AnimatedVisibility(selectedChatItems.value != null) {
|
||||
if (chatInfo != null) {
|
||||
SelectedItemsButtonsToolbar(
|
||||
contentTag = contentTag,
|
||||
chatsCtx = chatsCtx,
|
||||
selectedChatItems = selectedChatItems,
|
||||
chatInfo = chatInfo,
|
||||
deleteItems = { _ ->
|
||||
@@ -842,7 +833,7 @@ fun ChatLayout(
|
||||
}
|
||||
val reportsCount = reportsCount(chatInfo?.id)
|
||||
if (oneHandUI.value && chatBottomBar.value) {
|
||||
if (contentTag == null && reportsCount > 0) {
|
||||
if (chatsCtx.contentTag == null && reportsCount > 0) {
|
||||
ReportedCountToolbar(reportsCount, withStatusBar = true, showGroupReports)
|
||||
} else {
|
||||
StatusBarBackground()
|
||||
@@ -850,14 +841,14 @@ fun ChatLayout(
|
||||
} else {
|
||||
NavigationBarBackground(true, oneHandUI.value, noAlpha = true)
|
||||
}
|
||||
if (contentTag == MsgContentTag.Report) {
|
||||
if (chatsCtx.contentTag == MsgContentTag.Report) {
|
||||
if (oneHandUI.value) {
|
||||
StatusBarBackground()
|
||||
}
|
||||
Column(if (oneHandUI.value) Modifier.align(Alignment.BottomStart).imePadding() else Modifier) {
|
||||
Box {
|
||||
if (selectedChatItems.value == null) {
|
||||
GroupReportsAppBar(contentTag, { ModalManager.end.closeModal() }, onSearchValueChanged)
|
||||
GroupReportsAppBar(chatsCtx, { ModalManager.end.closeModal() }, onSearchValueChanged)
|
||||
} else {
|
||||
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value)
|
||||
}
|
||||
@@ -868,13 +859,13 @@ fun ChatLayout(
|
||||
Box {
|
||||
if (selectedChatItems.value == null) {
|
||||
if (chatInfo != null) {
|
||||
ChatInfoToolbar(chatInfo, contentTag, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
|
||||
ChatInfoToolbar(chatsCtx, chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
|
||||
}
|
||||
} else {
|
||||
SelectedItemsCounterToolbar(selectedChatItems, !oneHandUI.value || !chatBottomBar.value)
|
||||
}
|
||||
}
|
||||
if (contentTag == null && reportsCount > 0 && (!oneHandUI.value || !chatBottomBar.value)) {
|
||||
if (chatsCtx.contentTag == null && reportsCount > 0 && (!oneHandUI.value || !chatBottomBar.value)) {
|
||||
ReportedCountToolbar(reportsCount, withStatusBar = false, showGroupReports)
|
||||
}
|
||||
}
|
||||
@@ -886,8 +877,8 @@ fun ChatLayout(
|
||||
|
||||
@Composable
|
||||
fun BoxScope.ChatInfoToolbar(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chatInfo: ChatInfo,
|
||||
contentTag: MsgContentTag?,
|
||||
back: () -> Unit,
|
||||
info: () -> Unit,
|
||||
startCall: (CallMediaType) -> Unit,
|
||||
@@ -909,7 +900,7 @@ fun BoxScope.ChatInfoToolbar(
|
||||
showSearch.value = false
|
||||
}
|
||||
}
|
||||
if (appPlatform.isAndroid && contentTag == null) {
|
||||
if (appPlatform.isAndroid && chatsCtx.contentTag == null) {
|
||||
BackHandler(onBack = onBackClicked)
|
||||
}
|
||||
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
|
||||
@@ -1148,6 +1139,7 @@ private var reportsListState: LazyListState? = null
|
||||
|
||||
@Composable
|
||||
fun BoxScope.ChatItemsList(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
remoteHostId: Long?,
|
||||
chatInfo: ChatInfo,
|
||||
unreadCount: State<Int>,
|
||||
@@ -1205,107 +1197,107 @@ fun BoxScope.ChatItemsList(
|
||||
val searchValueIsEmpty = remember { derivedStateOf { searchValue.value.isEmpty() } }
|
||||
val searchValueIsNotBlank = remember { derivedStateOf { searchValue.value.isNotBlank() } }
|
||||
val revealedItems = rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(setOf<Long>()) }
|
||||
val contentTag = LocalContentTag.current
|
||||
// not using reversedChatItems inside to prevent possible derivedState bug in Compose when one derived state access can cause crash asking another derived state
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val mergedItems = remember {
|
||||
derivedStateOf {
|
||||
MergedItems.create(chatsCtx.chatItems.value.asReversed(), unreadCount, revealedItems.value, chatsCtx.chatState)
|
||||
if (chatsCtx != null) {
|
||||
val mergedItems = remember {
|
||||
derivedStateOf {
|
||||
MergedItems.create(chatsCtx.chatItems.value.asReversed(), unreadCount, revealedItems.value, chatsCtx.chatState)
|
||||
}
|
||||
}
|
||||
}
|
||||
val reversedChatItems = remember { derivedStateOf { chatsCtx.chatItems.value.asReversed() } }
|
||||
val reportsCount = reportsCount(chatInfo.id)
|
||||
val topPaddingToContent = topPaddingToContent(chatView = contentTag == null, contentTag == null && reportsCount > 0)
|
||||
val topPaddingToContentPx = rememberUpdatedState(with(LocalDensity.current) { topPaddingToContent.roundToPx() })
|
||||
val numberOfBottomAppBars = numberOfBottomAppBars()
|
||||
/** determines height based on window info and static height of two AppBars. It's needed because in the first graphic frame height of
|
||||
* [composeViewHeight] is unknown, but we need to set scroll position for unread messages already so it will be correct before the first frame appears
|
||||
* */
|
||||
val maxHeightForList = rememberUpdatedState(
|
||||
with(LocalDensity.current) { LocalWindowHeight().roundToPx() - topPaddingToContentPx.value - (AppBarHeight * fontSizeSqrtMultiplier * numberOfBottomAppBars).roundToPx() }
|
||||
)
|
||||
val resetListState = remember { mutableStateOf(false) }
|
||||
remember(chatModel.openAroundItemId.value) {
|
||||
if (chatModel.openAroundItemId.value != null) {
|
||||
closeSearch()
|
||||
resetListState.value = !resetListState.value
|
||||
val reversedChatItems = remember { derivedStateOf { chatsCtx.chatItems.value.asReversed() } }
|
||||
val reportsCount = reportsCount(chatInfo.id)
|
||||
val topPaddingToContent = topPaddingToContent(
|
||||
chatView = chatsCtx.contentTag == null,
|
||||
additionalTopBar = chatsCtx.contentTag == null && reportsCount > 0
|
||||
)
|
||||
val topPaddingToContentPx = rememberUpdatedState(with(LocalDensity.current) { topPaddingToContent.roundToPx() })
|
||||
val numberOfBottomAppBars = numberOfBottomAppBars()
|
||||
|
||||
/** determines height based on window info and static height of two AppBars. It's needed because in the first graphic frame height of
|
||||
* [composeViewHeight] is unknown, but we need to set scroll position for unread messages already so it will be correct before the first frame appears
|
||||
* */
|
||||
val maxHeightForList = rememberUpdatedState(
|
||||
with(LocalDensity.current) { LocalWindowHeight().roundToPx() - topPaddingToContentPx.value - (AppBarHeight * fontSizeSqrtMultiplier * numberOfBottomAppBars).roundToPx() }
|
||||
)
|
||||
val resetListState = remember { mutableStateOf(false) }
|
||||
remember(chatModel.openAroundItemId.value) {
|
||||
if (chatModel.openAroundItemId.value != null) {
|
||||
closeSearch()
|
||||
resetListState.value = !resetListState.value
|
||||
}
|
||||
}
|
||||
}
|
||||
val highlightedItems = remember { mutableStateOf(setOf<Long>()) }
|
||||
val hoveredItemId = remember { mutableStateOf(null as Long?) }
|
||||
val listState = rememberUpdatedState(rememberSaveable(chatInfo.id, searchValueIsEmpty.value, resetListState.value, saver = LazyListState.Saver) {
|
||||
val openAroundItemId = chatModel.openAroundItemId.value
|
||||
val index = mergedItems.value.indexInParentItems[openAroundItemId] ?: mergedItems.value.items.indexOfLast { it.hasUnread() }
|
||||
val reportsState = reportsListState
|
||||
if (openAroundItemId != null) {
|
||||
highlightedItems.value += openAroundItemId
|
||||
chatModel.openAroundItemId.value = null
|
||||
val highlightedItems = remember { mutableStateOf(setOf<Long>()) }
|
||||
val hoveredItemId = remember { mutableStateOf(null as Long?) }
|
||||
val listState = rememberUpdatedState(rememberSaveable(chatInfo.id, searchValueIsEmpty.value, resetListState.value, saver = LazyListState.Saver) {
|
||||
val openAroundItemId = chatModel.openAroundItemId.value
|
||||
val index = mergedItems.value.indexInParentItems[openAroundItemId] ?: mergedItems.value.items.indexOfLast { it.hasUnread() }
|
||||
val reportsState = reportsListState
|
||||
if (openAroundItemId != null) {
|
||||
highlightedItems.value += openAroundItemId
|
||||
chatModel.openAroundItemId.value = null
|
||||
}
|
||||
hoveredItemId.value = null
|
||||
if (reportsState != null) {
|
||||
reportsListState = null
|
||||
reportsState
|
||||
} else if (index <= 0 || !searchValueIsEmpty.value) {
|
||||
LazyListState(0, 0)
|
||||
} else {
|
||||
LazyListState(index + 1, -maxHeightForList.value)
|
||||
}
|
||||
})
|
||||
SaveReportsStateOnDispose(chatsCtx, listState)
|
||||
val maxHeight = remember { derivedStateOf { listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value } }
|
||||
val loadingMoreItems = remember { mutableStateOf(false) }
|
||||
val animatedScrollingInProgress = remember { mutableStateOf(false) }
|
||||
val ignoreLoadingRequests = remember(remoteHostId) { mutableSetOf<Long>() }
|
||||
LaunchedEffect(chatInfo.id, searchValueIsEmpty.value) {
|
||||
if (searchValueIsEmpty.value && reversedChatItems.value.size < ChatPagination.INITIAL_COUNT)
|
||||
ignoreLoadingRequests.add(reversedChatItems.value.lastOrNull()?.id ?: return@LaunchedEffect)
|
||||
}
|
||||
hoveredItemId.value = null
|
||||
if (reportsState != null) {
|
||||
reportsListState = null
|
||||
reportsState
|
||||
} else if (index <= 0 || !searchValueIsEmpty.value) {
|
||||
LazyListState(0, 0)
|
||||
} else {
|
||||
LazyListState(index + 1, -maxHeightForList.value)
|
||||
}
|
||||
})
|
||||
SaveReportsStateOnDispose(listState)
|
||||
val maxHeight = remember { derivedStateOf { listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value } }
|
||||
val loadingMoreItems = remember { mutableStateOf(false) }
|
||||
val animatedScrollingInProgress = remember { mutableStateOf(false) }
|
||||
val ignoreLoadingRequests = remember(remoteHostId) { mutableSetOf<Long>() }
|
||||
LaunchedEffect(chatInfo.id, searchValueIsEmpty.value) {
|
||||
if (searchValueIsEmpty.value && reversedChatItems.value.size < ChatPagination.INITIAL_COUNT)
|
||||
ignoreLoadingRequests.add(reversedChatItems.value.lastOrNull()?.id ?: return@LaunchedEffect)
|
||||
}
|
||||
PreloadItems(chatInfo.id, if (searchValueIsEmpty.value) ignoreLoadingRequests else mutableSetOf(), loadingMoreItems, resetListState, contentTag, mergedItems, listState, ChatPagination.UNTIL_PRELOAD_COUNT) { chatId, pagination ->
|
||||
if (loadingMoreItems.value || chatId != chatModel.chatId.value) return@PreloadItems false
|
||||
loadingMoreItems.value = true
|
||||
withContext(NonCancellable) {
|
||||
try {
|
||||
loadMessages(chatId, pagination) {
|
||||
visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value)
|
||||
PreloadItems(chatsCtx, chatInfo.id, if (searchValueIsEmpty.value) ignoreLoadingRequests else mutableSetOf(), loadingMoreItems, resetListState, mergedItems, listState, ChatPagination.UNTIL_PRELOAD_COUNT) { chatId, pagination ->
|
||||
if (loadingMoreItems.value || chatId != chatModel.chatId.value) return@PreloadItems false
|
||||
loadingMoreItems.value = true
|
||||
withContext(NonCancellable) {
|
||||
try {
|
||||
loadMessages(chatId, pagination) {
|
||||
visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value)
|
||||
}
|
||||
} finally {
|
||||
loadingMoreItems.value = false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
val remoteHostIdUpdated = rememberUpdatedState(remoteHostId)
|
||||
val chatInfoUpdated = rememberUpdatedState(chatInfo)
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollToItem: (Long) -> Unit = remember {
|
||||
// In group reports just set the itemId to scroll to so the main ChatView will handle scrolling
|
||||
if (chatsCtx.contentTag == MsgContentTag.Report) return@remember { scrollToItemId.value = it }
|
||||
scrollToItem(searchValue, loadingMoreItems, animatedScrollingInProgress, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages)
|
||||
}
|
||||
val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(chatsCtx, remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem) }
|
||||
if (chatsCtx.contentTag == null) {
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { scrollToItemId.value }.filterNotNull().collect {
|
||||
if (appPlatform.isAndroid) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
scrollToItem(it)
|
||||
scrollToItemId.value = null
|
||||
}
|
||||
} finally {
|
||||
loadingMoreItems.value = false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
SmallScrollOnNewMessage(listState, reversedChatItems)
|
||||
val finishedInitialComposition = remember { mutableStateOf(false) }
|
||||
NotifyChatListOnFinishingComposition(finishedInitialComposition, chatInfo, revealedItems, listState, onComposed)
|
||||
|
||||
val remoteHostIdUpdated = rememberUpdatedState(remoteHostId)
|
||||
val chatInfoUpdated = rememberUpdatedState(chatInfo)
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollToItem: (Long) -> Unit = remember {
|
||||
// In group reports just set the itemId to scroll to so the main ChatView will handle scrolling
|
||||
if (contentTag == MsgContentTag.Report) return@remember { scrollToItemId.value = it }
|
||||
scrollToItem(searchValue, loadingMoreItems, animatedScrollingInProgress, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages)
|
||||
}
|
||||
val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem, contentTag) }
|
||||
if (contentTag == null) {
|
||||
LaunchedEffect(Unit) { snapshotFlow { scrollToItemId.value }.filterNotNull().collect {
|
||||
if (appPlatform.isAndroid) {
|
||||
ModalManager.end.closeModals()
|
||||
DisposableEffectOnGone(
|
||||
whenGone = {
|
||||
VideoPlayerHolder.releaseAll()
|
||||
}
|
||||
scrollToItem(it)
|
||||
scrollToItemId.value = null }
|
||||
}
|
||||
}
|
||||
SmallScrollOnNewMessage(listState, reversedChatItems)
|
||||
val finishedInitialComposition = remember { mutableStateOf(false) }
|
||||
NotifyChatListOnFinishingComposition(finishedInitialComposition, chatInfo, revealedItems, listState, onComposed)
|
||||
|
||||
DisposableEffectOnGone(
|
||||
always = {
|
||||
chatsCtx.chatItemsChangesListener = recalculateChatStatePositions(chatsCtx.chatState)
|
||||
},
|
||||
whenGone = {
|
||||
VideoPlayerHolder.releaseAll()
|
||||
chatsCtx.chatItemsChangesListener = recalculateChatStatePositions(chatsCtx.chatState)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ChatViewListItem(
|
||||
@@ -1350,7 +1342,7 @@ fun BoxScope.ChatItemsList(
|
||||
highlightedItems.value = setOf()
|
||||
}
|
||||
}
|
||||
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, hoveredItemId = hoveredItemId, range = range, searchIsNotBlank = searchValueIsNotBlank, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems, reversedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, archiveReports = archiveReports, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||
ChatItemView(chatsCtx, remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, hoveredItemId = hoveredItemId, range = range, searchIsNotBlank = searchValueIsNotBlank, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems, reversedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, archiveReports = archiveReports, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1487,7 +1479,7 @@ fun BoxScope.ChatItemsList(
|
||||
}
|
||||
} else {
|
||||
ChatItemBox {
|
||||
AnimatedVisibility (selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
AnimatedVisibility(selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
SelectedListItem(Modifier.padding(start = 8.dp), cItem.id, selectedChatItems)
|
||||
}
|
||||
Row(
|
||||
@@ -1502,7 +1494,7 @@ fun BoxScope.ChatItemsList(
|
||||
}
|
||||
} else {
|
||||
ChatItemBox {
|
||||
AnimatedVisibility (selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
AnimatedVisibility(selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
SelectedListItem(Modifier.padding(start = 8.dp), cItem.id, selectedChatItems)
|
||||
}
|
||||
Box(
|
||||
@@ -1517,7 +1509,7 @@ fun BoxScope.ChatItemsList(
|
||||
}
|
||||
} else { // direct message
|
||||
ChatItemBox {
|
||||
AnimatedVisibility (selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
AnimatedVisibility(selectionVisible, enter = fadeIn(), exit = fadeOut()) {
|
||||
SelectedListItem(Modifier.padding(start = 8.dp), cItem.id, selectedChatItems)
|
||||
}
|
||||
|
||||
@@ -1547,110 +1539,110 @@ fun BoxScope.ChatItemsList(
|
||||
ChatItemView(cItem, range, itemSeparation, previousItemSeparationLargeGap)
|
||||
}
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.align(Alignment.BottomCenter),
|
||||
state = listState.value,
|
||||
contentPadding = PaddingValues(
|
||||
top = topPaddingToContent,
|
||||
bottom = composeViewHeight.value
|
||||
),
|
||||
reverseLayout = true,
|
||||
additionalBarOffset = composeViewHeight,
|
||||
additionalTopBar = rememberUpdatedState(contentTag == null && reportsCount > 0),
|
||||
chatBottomBar = remember { appPrefs.chatBottomBar.state }
|
||||
) {
|
||||
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, listItem.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 }
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.align(Alignment.BottomCenter),
|
||||
state = listState.value,
|
||||
contentPadding = PaddingValues(
|
||||
top = topPaddingToContent,
|
||||
bottom = composeViewHeight.value
|
||||
),
|
||||
reverseLayout = true,
|
||||
additionalBarOffset = composeViewHeight,
|
||||
additionalTopBar = rememberUpdatedState(chatsCtx.contentTag == null && reportsCount > 0),
|
||||
chatBottomBar = remember { appPrefs.chatBottomBar.state }
|
||||
) {
|
||||
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, listItem.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
FloatingButtons(
|
||||
reversedChatItems,
|
||||
chatInfoUpdated,
|
||||
topPaddingToContent,
|
||||
topPaddingToContentPx,
|
||||
contentTag,
|
||||
loadingMoreItems,
|
||||
loadingTopItems,
|
||||
loadingBottomItems,
|
||||
animatedScrollingInProgress,
|
||||
mergedItems,
|
||||
unreadCount,
|
||||
maxHeight,
|
||||
composeViewHeight,
|
||||
searchValue,
|
||||
markChatRead,
|
||||
listState,
|
||||
loadMessages
|
||||
)
|
||||
FloatingDate(Modifier.padding(top = 10.dp + topPaddingToContent).align(Alignment.TopCenter), topPaddingToContentPx, mergedItems, listState)
|
||||
FloatingButtons(
|
||||
chatsCtx,
|
||||
reversedChatItems,
|
||||
chatInfoUpdated,
|
||||
topPaddingToContent,
|
||||
topPaddingToContentPx,
|
||||
loadingMoreItems,
|
||||
loadingTopItems,
|
||||
loadingBottomItems,
|
||||
animatedScrollingInProgress,
|
||||
mergedItems,
|
||||
unreadCount,
|
||||
maxHeight,
|
||||
composeViewHeight,
|
||||
searchValue,
|
||||
markChatRead,
|
||||
listState,
|
||||
loadMessages
|
||||
)
|
||||
FloatingDate(Modifier.padding(top = 10.dp + topPaddingToContent).align(Alignment.TopCenter), topPaddingToContentPx, mergedItems, listState)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { listState.value.isScrollInProgress }
|
||||
.collect {
|
||||
chatViewScrollState.value = it
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { listState.value.isScrollInProgress }
|
||||
.filter { !it }
|
||||
.collect {
|
||||
if (animatedScrollingInProgress.value) {
|
||||
animatedScrollingInProgress.value = false
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { listState.value.isScrollInProgress }
|
||||
.collect {
|
||||
chatViewScrollState.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { listState.value.isScrollInProgress }
|
||||
.filter { !it }
|
||||
.collect {
|
||||
if (animatedScrollingInProgress.value) {
|
||||
animatedScrollingInProgress.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadLastItems(chatId: State<ChatId>, contentTag: MsgContentTag?, listState: State<LazyListState>, loadItems: State<suspend (ChatId, ChatPagination) -> Boolean>) {
|
||||
private suspend fun loadLastItems(chatsCtx: ChatModel.ChatsContext, chatId: State<ChatId>, listState: State<LazyListState>, loadItems: State<suspend (ChatId, ChatPagination) -> Boolean>) {
|
||||
val lastVisible = listState.value.layoutInfo.visibleItemsInfo.lastOrNull()
|
||||
val itemsCanCoverScreen = lastVisible != null && listState.value.layoutInfo.viewportEndOffset - listState.value.layoutInfo.afterContentPadding <= lastVisible.offset + lastVisible.size
|
||||
if (!itemsCanCoverScreen) return
|
||||
|
||||
if (lastItemsLoaded(contentTag)) return
|
||||
if (lastItemsLoaded(chatsCtx)) return
|
||||
|
||||
delay(500)
|
||||
loadItems.value(chatId.value, ChatPagination.Last(ChatPagination.INITIAL_COUNT))
|
||||
}
|
||||
|
||||
private fun lastItemsLoaded(contentTag: MsgContentTag?): Boolean {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
private fun lastItemsLoaded(chatsCtx: ChatModel.ChatsContext): Boolean {
|
||||
val chatState = chatsCtx.chatState
|
||||
return chatState.splits.value.isEmpty() || chatState.splits.value.firstOrNull() != chatsCtx.chatItems.value.lastOrNull()?.id
|
||||
}
|
||||
@@ -1715,11 +1707,11 @@ private fun NotifyChatListOnFinishingComposition(
|
||||
|
||||
@Composable
|
||||
fun BoxScope.FloatingButtons(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
reversedChatItems: State<List<ChatItem>>,
|
||||
chatInfo: State<ChatInfo>,
|
||||
topPaddingToContent: Dp,
|
||||
topPaddingToContentPx: State<Int>,
|
||||
contentTag: MsgContentTag?,
|
||||
loadingMoreItems: MutableState<Boolean>,
|
||||
loadingTopItems: MutableState<Boolean>,
|
||||
loadingBottomItems: MutableState<Boolean>,
|
||||
@@ -1743,7 +1735,6 @@ fun BoxScope.FloatingButtons(
|
||||
fun scrollToTopUnread() {
|
||||
scope.launch {
|
||||
tryBlockAndSetLoadingMore(loadingMoreItems) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
if (chatsCtx.chatState.splits.value.isNotEmpty()) {
|
||||
val pagination = ChatPagination.Initial(ChatPagination.INITIAL_COUNT)
|
||||
val oldSize = reversedChatItems.value.size
|
||||
@@ -1806,7 +1797,7 @@ fun BoxScope.FloatingButtons(
|
||||
animatedScrollingInProgress,
|
||||
composeViewHeight,
|
||||
onClick = {
|
||||
if (loadingBottomItems.value || !lastItemsLoaded(contentTag)) {
|
||||
if (loadingBottomItems.value || !lastItemsLoaded(chatsCtx)) {
|
||||
requestedTopScroll.value = false
|
||||
requestedBottomScroll.value = true
|
||||
} else {
|
||||
@@ -1881,11 +1872,11 @@ fun BoxScope.FloatingButtons(
|
||||
|
||||
@Composable
|
||||
fun PreloadItems(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chatId: String,
|
||||
ignoreLoadingRequests: MutableSet<Long>,
|
||||
loadingMoreItems: State<Boolean>,
|
||||
resetListState: State<Boolean>,
|
||||
contentTag: MsgContentTag?,
|
||||
mergedItems: State<MergedItems>,
|
||||
listState: State<LazyListState>,
|
||||
remaining: Int,
|
||||
@@ -1911,20 +1902,20 @@ fun PreloadItems(
|
||||
snapshotFlow { listState.value.firstVisibleItemIndex }
|
||||
.distinctUntilChanged()
|
||||
.collect { firstVisibleIndex ->
|
||||
if (!preloadItemsBefore(firstVisibleIndex, chatId, ignoreLoadingRequests, contentTag, mergedItems, listState, remaining, loadItems)) {
|
||||
preloadItemsAfter(firstVisibleIndex, chatId, contentTag, mergedItems, remaining, loadItems)
|
||||
if (!preloadItemsBefore(chatsCtx, firstVisibleIndex, chatId, ignoreLoadingRequests, mergedItems, listState, remaining, loadItems)) {
|
||||
preloadItemsAfter(chatsCtx, firstVisibleIndex, chatId, mergedItems, remaining, loadItems)
|
||||
}
|
||||
loadLastItems(chatId, contentTag, listState, loadItems)
|
||||
loadLastItems(chatsCtx, chatId, listState, loadItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun preloadItemsBefore(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
firstVisibleIndex: Int,
|
||||
chatId: State<String>,
|
||||
ignoreLoadingRequests: State<MutableSet<Long>>,
|
||||
contentTag: MsgContentTag?,
|
||||
mergedItems: State<MergedItems>,
|
||||
listState: State<LazyListState>,
|
||||
remaining: Int,
|
||||
@@ -1933,18 +1924,18 @@ private suspend fun preloadItemsBefore(
|
||||
val splits = mergedItems.value.splits
|
||||
val lastVisibleIndex = (listState.value.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0)
|
||||
var lastIndexToLoadFrom: Int? = findLastIndexToLoadFromInSplits(firstVisibleIndex, lastVisibleIndex, remaining, splits)
|
||||
val items = reversedChatItemsStatic(contentTag)
|
||||
val items = reversedChatItemsStatic(chatsCtx)
|
||||
if (splits.isEmpty() && items.isNotEmpty() && lastVisibleIndex > mergedItems.value.items.size - remaining) {
|
||||
lastIndexToLoadFrom = items.lastIndex
|
||||
}
|
||||
if (lastIndexToLoadFrom != null) {
|
||||
val loadFromItemId = items.getOrNull(lastIndexToLoadFrom)?.id ?: return false
|
||||
if (!ignoreLoadingRequests.value.contains(loadFromItemId)) {
|
||||
val items = reversedChatItemsStatic(contentTag)
|
||||
val items = reversedChatItemsStatic(chatsCtx)
|
||||
val sizeWas = items.size
|
||||
val oldestItemIdWas = items.lastOrNull()?.id
|
||||
val triedToLoad = loadItems.value(chatId.value, ChatPagination.Before(loadFromItemId, ChatPagination.PRELOAD_COUNT))
|
||||
val itemsUpdated = reversedChatItemsStatic(contentTag)
|
||||
val itemsUpdated = reversedChatItemsStatic(chatsCtx)
|
||||
if (triedToLoad && sizeWas == itemsUpdated.size && oldestItemIdWas == itemsUpdated.lastOrNull()?.id) {
|
||||
ignoreLoadingRequests.value.add(loadFromItemId)
|
||||
return false
|
||||
@@ -1956,14 +1947,14 @@ private suspend fun preloadItemsBefore(
|
||||
}
|
||||
|
||||
private suspend fun preloadItemsAfter(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
firstVisibleIndex: Int,
|
||||
chatId: State<String>,
|
||||
contentTag: MsgContentTag?,
|
||||
mergedItems: State<MergedItems>,
|
||||
remaining: Int,
|
||||
loadItems: State<suspend (ChatId, ChatPagination) -> Boolean>,
|
||||
) {
|
||||
val items = reversedChatItemsStatic(contentTag)
|
||||
val items = reversedChatItemsStatic(chatsCtx)
|
||||
val splits = mergedItems.value.splits
|
||||
val split = splits.lastOrNull { it.indexRangeInParentItems.contains(firstVisibleIndex) }
|
||||
// we're inside a splitRange (top --- [end of the splitRange --- we're here --- start of the splitRange] --- bottom)
|
||||
@@ -2129,11 +2120,10 @@ private fun FloatingDate(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SaveReportsStateOnDispose(listState: State<LazyListState>) {
|
||||
val contentTag = LocalContentTag.current
|
||||
private fun SaveReportsStateOnDispose(chatsCtx: ChatModel.ChatsContext, listState: State<LazyListState>) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
reportsListState = if (contentTag == MsgContentTag.Report && ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) listState.value else null
|
||||
reportsListState = if (chatsCtx.contentTag == MsgContentTag.Report && ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) listState.value else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2244,10 +2234,8 @@ fun reportsCount(staleChatId: String?): Int {
|
||||
}
|
||||
}
|
||||
|
||||
private fun reversedChatItemsStatic(contentTag: MsgContentTag?): List<ChatItem> {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
return chatsCtx.chatItems.value.asReversed()
|
||||
}
|
||||
private fun reversedChatItemsStatic(chatsCtx: ChatModel.ChatsContext): List<ChatItem> =
|
||||
chatsCtx.chatItems.value.asReversed()
|
||||
|
||||
private fun oldestPartiallyVisibleListItemInListStateOrNull(topPaddingToContentPx: State<Int>, mergedItems: State<MergedItems>, listState: State<LazyListState>): ListItem? {
|
||||
val lastFullyVisibleOffset = listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value
|
||||
@@ -2323,22 +2311,20 @@ private fun scrollToItem(
|
||||
}
|
||||
|
||||
private fun findQuotedItemFromItem(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: State<Long?>,
|
||||
chatInfo: State<ChatInfo>,
|
||||
scope: CoroutineScope,
|
||||
scrollToItem: (Long) -> Unit,
|
||||
contentTag: MsgContentTag?
|
||||
scrollToItem: (Long) -> Unit
|
||||
): (Long) -> Unit = { itemId: Long ->
|
||||
scope.launch(Dispatchers.Default) {
|
||||
val item = apiLoadSingleMessage(rhId.value, chatInfo.value.chatType, chatInfo.value.apiId, itemId, contentTag)
|
||||
val item = apiLoadSingleMessage(chatsCtx, rhId.value, chatInfo.value.chatType, chatInfo.value.apiId, itemId)
|
||||
if (item != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatItem(chatInfo.value, item)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(chatInfo.value, item)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.updateChatItem(chatInfo.value, item)
|
||||
}
|
||||
if (item.quotedItem?.itemId != null) {
|
||||
scrollToItem(item.quotedItem.itemId)
|
||||
@@ -2533,15 +2519,13 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.value?.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2577,15 +2561,13 @@ private fun archiveReports(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
chatModel.secondaryChatsContext.value?.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
chatModel.secondaryChatsContext.value?.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2938,6 +2920,7 @@ fun PreviewChatLayout() {
|
||||
val unreadCount = remember { mutableStateOf(chatItems.count { it.isRcvNew }) }
|
||||
val searchValue = remember { mutableStateOf("") }
|
||||
ChatLayout(
|
||||
chatsCtx = ChatModel.ChatsContext(contentTag = null),
|
||||
remoteHostId = remember { mutableStateOf(null) },
|
||||
chatInfo = remember { mutableStateOf(ChatInfo.Direct.sampleData) },
|
||||
unreadCount = unreadCount,
|
||||
@@ -3015,6 +2998,7 @@ fun PreviewGroupChatLayout() {
|
||||
val unreadCount = remember { mutableStateOf(chatItems.count { it.isRcvNew }) }
|
||||
val searchValue = remember { mutableStateOf("") }
|
||||
ChatLayout(
|
||||
chatsCtx = ChatModel.ChatsContext(contentTag = null),
|
||||
remoteHostId = remember { mutableStateOf(null) },
|
||||
chatInfo = remember { mutableStateOf(ChatInfo.Direct.sampleData) },
|
||||
unreadCount = unreadCount,
|
||||
|
||||
+1
-3
@@ -15,7 +15,6 @@ import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.BackHandler
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.res.MR
|
||||
@@ -60,8 +59,8 @@ private fun SelectAllButton(onClick: () -> Unit) {
|
||||
|
||||
@Composable
|
||||
fun SelectedItemsButtonsToolbar(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chatInfo: ChatInfo,
|
||||
contentTag: MsgContentTag?,
|
||||
selectedChatItems: MutableState<Set<Long>?>,
|
||||
deleteItems: (Boolean) -> Unit, // Boolean - delete for everyone is possible
|
||||
archiveItems: () -> Unit,
|
||||
@@ -122,7 +121,6 @@ fun SelectedItemsButtonsToolbar(
|
||||
}
|
||||
Divider(Modifier.align(Alignment.TopStart))
|
||||
}
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val chatItems = remember { derivedStateOf { chatsCtx.chatItems.value } }
|
||||
LaunchedEffect(chatInfo, chatItems.value, selectedChatItems.value) {
|
||||
recheckItems(chatInfo, chatItems.value, selectedChatItems, deleteEnabled, deleteForEveryoneEnabled, canArchiveReports, canModerate, moderateEnabled, forwardEnabled, deleteCountProhibited, forwardCountProhibited)
|
||||
|
||||
+1
-3
@@ -65,9 +65,7 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
|
||||
+4
-5
@@ -51,6 +51,7 @@ val MEMBER_ROW_VERTICAL_PADDING = 8.dp
|
||||
|
||||
@Composable
|
||||
fun ModalData.GroupChatInfoView(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
chatId: String,
|
||||
groupLink: CreatedConnLink?,
|
||||
@@ -92,7 +93,7 @@ fun ModalData.GroupChatInfoView(
|
||||
val previousChatTTL = chatItemTTL.value
|
||||
chatItemTTL.value = it
|
||||
|
||||
setChatTTLAlert(chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
|
||||
setChatTTLAlert(chatsCtx, chat.remoteHostId, chat.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
|
||||
},
|
||||
activeSortedMembers = remember { chatModel.groupMembers }.value
|
||||
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
|
||||
@@ -971,10 +972,8 @@ fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, onSu
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
||||
+13
-31
@@ -66,9 +66,7 @@ fun GroupMemberInfoView(
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
@@ -88,7 +86,7 @@ fun GroupMemberInfoView(
|
||||
getContactChat = { chatModel.getContactChat(it) },
|
||||
openDirectChat = {
|
||||
withBGApi {
|
||||
apiLoadMessages(rhId, ChatType.Direct, it, null, ChatPagination.Initial(ChatPagination.INITIAL_COUNT))
|
||||
apiLoadMessages(chatModel.chatsContext, rhId, ChatType.Direct, it, ChatPagination.Initial(ChatPagination.INITIAL_COUNT))
|
||||
if (chatModel.getContactChat(it) != null) {
|
||||
closeAll()
|
||||
}
|
||||
@@ -154,9 +152,7 @@ fun GroupMemberInfoView(
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
@@ -173,9 +169,7 @@ fun GroupMemberInfoView(
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
@@ -195,9 +189,7 @@ fun GroupMemberInfoView(
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
@@ -223,9 +215,7 @@ fun GroupMemberInfoView(
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
r
|
||||
}
|
||||
@@ -262,10 +252,8 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
removedMembers.forEach { removedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
removedMembers.forEach { removedMember ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -714,10 +702,8 @@ fun updateMembersRole(newRole: GroupMemberRole, rhId: Long?, groupInfo: GroupInf
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
@@ -815,9 +801,7 @@ fun updateMemberSettings(rhId: Long?, gInfo: GroupInfo, member: GroupMember, mem
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,10 +862,8 @@ fun blockMemberForAll(rhId: Long?, gInfo: GroupInfo, memberIds: List<Long>, bloc
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.value?.upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
||||
+11
-12
@@ -14,16 +14,14 @@ import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
val LocalContentTag: ProvidableCompositionLocal<MsgContentTag?> = staticCompositionLocalOf { null }
|
||||
|
||||
@Composable
|
||||
private fun GroupReportsView(staleChatId: State<String?>, scrollToItemId: MutableState<Long?>) {
|
||||
ChatView(staleChatId, contentTag = MsgContentTag.Report, scrollToItemId, onComposed = {})
|
||||
private fun GroupReportsView(reportsChatsCtx: ChatModel.ChatsContext, staleChatId: State<String?>, scrollToItemId: MutableState<Long?>) {
|
||||
ChatView(reportsChatsCtx, staleChatId, scrollToItemId, onComposed = {})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GroupReportsAppBar(
|
||||
contentTag: MsgContentTag?,
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
close: () -> Unit,
|
||||
onSearchValueChanged: (String) -> Unit
|
||||
) {
|
||||
@@ -51,11 +49,11 @@ fun GroupReportsAppBar(
|
||||
}
|
||||
}
|
||||
)
|
||||
ItemsReload(contentTag)
|
||||
ItemsReload(chatsCtx)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ItemsReload(contentTag: MsgContentTag?) {
|
||||
private fun ItemsReload(chatsCtx: ChatModel.ChatsContext,) {
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { chatModel.chatId.value }
|
||||
.distinctUntilChanged()
|
||||
@@ -65,18 +63,19 @@ private fun ItemsReload(contentTag: MsgContentTag?) {
|
||||
.filterNotNull()
|
||||
.filter { it.chatInfo is ChatInfo.Group }
|
||||
.collect { chat ->
|
||||
reloadItems(chat, contentTag)
|
||||
reloadItems(chatsCtx, chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun showGroupReportsView(staleChatId: State<String?>, scrollToItemId: MutableState<Long?>, chatInfo: ChatInfo) {
|
||||
openChat(chatModel.remoteHostId(), chatInfo, MsgContentTag.Report)
|
||||
val reportsChatsCtx = ChatModel.ChatsContext(contentTag = MsgContentTag.Report)
|
||||
openChat(secondaryChatsCtx = reportsChatsCtx, chatModel.remoteHostId(), chatInfo)
|
||||
ModalManager.end.showCustomModal(true, id = ModalViewId.SECONDARY_CHAT) { close ->
|
||||
ModalView({}, showAppBar = false) {
|
||||
val chatInfo = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chatModel.chatId.value }?.chatInfo } }.value
|
||||
if (chatInfo is ChatInfo.Group && chatInfo.groupInfo.canModerate) {
|
||||
GroupReportsView(staleChatId, scrollToItemId)
|
||||
GroupReportsView(reportsChatsCtx, staleChatId, scrollToItemId)
|
||||
} else {
|
||||
LaunchedEffect(Unit) {
|
||||
close()
|
||||
@@ -86,6 +85,6 @@ suspend fun showGroupReportsView(staleChatId: State<String?>, scrollToItemId: Mu
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun reloadItems(chat: Chat, contentTag: MsgContentTag?) {
|
||||
apiLoadMessages(chat.remoteHostId, chat.chatInfo.chatType, chat.chatInfo.apiId, contentTag, ChatPagination.Initial(ChatPagination.INITIAL_COUNT))
|
||||
private suspend fun reloadItems(chatsCtx: ChatModel.ChatsContext, chat: Chat) {
|
||||
apiLoadMessages(chatsCtx, chat.remoteHostId, chat.chatInfo.chatType, chat.chatInfo.apiId, ChatPagination.Initial(ChatPagination.INITIAL_COUNT))
|
||||
}
|
||||
|
||||
+3
-6
@@ -12,12 +12,11 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.common.platform.onRightClick
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
|
||||
@Composable
|
||||
fun CIChatFeatureView(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
chatInfo: ChatInfo,
|
||||
chatItem: ChatItem,
|
||||
feature: Feature,
|
||||
@@ -26,7 +25,7 @@ fun CIChatFeatureView(
|
||||
revealed: State<Boolean>,
|
||||
showMenu: MutableState<Boolean>,
|
||||
) {
|
||||
val merged = if (!revealed.value) mergedFeatures(chatItem, chatInfo) else emptyList()
|
||||
val merged = if (!revealed.value) mergedFeatures(chatsCtx, chatItem, chatInfo) else emptyList()
|
||||
Box(
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
@@ -73,11 +72,9 @@ private fun Feature.toFeatureInfo(color: Color, param: Int?, type: String): Feat
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<FeatureInfo>? {
|
||||
val m = ChatModel
|
||||
private fun mergedFeatures(chatsCtx: ChatModel.ChatsContext, chatItem: ChatItem, chatInfo: ChatInfo): List<FeatureInfo>? {
|
||||
val fs: ArrayList<FeatureInfo> = arrayListOf()
|
||||
val icons: MutableSet<PainterBox> = mutableSetOf()
|
||||
val chatsCtx = if (LocalContentTag.current == null) m.chatsContext else m.secondaryChatsContext
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
var i = getChatItemIndexOrNull(chatItem, reversedChatItems)
|
||||
if (i != null) {
|
||||
|
||||
+25
-25
@@ -29,7 +29,6 @@ import chat.simplex.common.model.ChatModel.currentUser
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
import chat.simplex.common.views.chatlist.openChat
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
@@ -63,6 +62,7 @@ data class ChatItemReactionMenuItem (
|
||||
|
||||
@Composable
|
||||
fun ChatItemView(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
rhId: Long?,
|
||||
cInfo: ChatInfo,
|
||||
cItem: ChatItem,
|
||||
@@ -277,7 +277,7 @@ fun ChatItemView(
|
||||
if (searchIsNotBlank.value) {
|
||||
GoToItemInnerButton(alignStart, MR.images.ic_search, 17.dp, parentActivated) {
|
||||
withBGApi {
|
||||
openChat(rhId, cInfo.chatType, cInfo.apiId, null, cItem.id)
|
||||
openChat(secondaryChatsCtx = null, rhId, cInfo.chatType, cInfo.apiId, cItem.id)
|
||||
closeReportsIfNeeded()
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,7 @@ fun ChatItemView(
|
||||
GoToItemInnerButton(alignStart, MR.images.ic_arrow_forward, 22.dp, parentActivated) {
|
||||
val (chatType, apiId, msgId) = chatTypeApiIdMsgId
|
||||
withBGApi {
|
||||
openChat(rhId, chatType, apiId, null, msgId)
|
||||
openChat(secondaryChatsCtx = null, rhId, chatType, apiId, msgId)
|
||||
closeReportsIfNeeded()
|
||||
}
|
||||
}
|
||||
@@ -364,7 +364,7 @@ fun ChatItemView(
|
||||
@Composable
|
||||
fun DeleteItemMenu() {
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -382,7 +382,7 @@ fun ChatItemView(
|
||||
if (cItem.chatDir !is CIDirection.GroupSnd && cInfo.groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
|
||||
ArchiveReportItemAction(cItem.id, cInfo.groupInfo.membership.memberActive, showMenu, archiveReports)
|
||||
}
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages, buttonText = stringResource(MR.strings.delete_report))
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages, buttonText = stringResource(MR.strings.delete_report))
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
}
|
||||
@@ -472,7 +472,7 @@ fun ChatItemView(
|
||||
CancelFileItemAction(cItem.file.fileId, showMenu, cancelFile = cancelFile, cancelAction = cItem.file.cancelAction)
|
||||
}
|
||||
if (!(live && cItem.meta.isLive) && !preview) {
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
}
|
||||
if (cItem.chatDir !is CIDirection.GroupSnd) {
|
||||
val groupInfo = cItem.memberToModerate(cInfo)?.first
|
||||
@@ -498,7 +498,7 @@ fun ChatItemView(
|
||||
ExpandItemAction(revealed, showMenu, reveal)
|
||||
}
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -508,7 +508,7 @@ fun ChatItemView(
|
||||
cItem.isDeletedContent -> {
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -522,7 +522,7 @@ fun ChatItemView(
|
||||
} else {
|
||||
ExpandItemAction(revealed, showMenu, reveal)
|
||||
}
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -531,7 +531,7 @@ fun ChatItemView(
|
||||
}
|
||||
else -> {
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (selectedChatItems.value == null) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -548,7 +548,7 @@ fun ChatItemView(
|
||||
RevealItemAction(revealed, showMenu, reveal)
|
||||
}
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -560,7 +560,7 @@ fun ChatItemView(
|
||||
fun ContentItem() {
|
||||
val mc = cItem.content.msgContent
|
||||
if (cItem.meta.itemDeleted != null && (!revealed.value || cItem.isDeletedContent)) {
|
||||
MarkedDeletedItemView(cItem, cInfo, cInfo.timedMessagesTTL, revealed, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
MarkedDeletedItemView(chatsCtx, cItem, cInfo, cInfo.timedMessagesTTL, revealed, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
MarkedDeletedItemDropdownMenu()
|
||||
} else {
|
||||
if (cItem.quotedItem == null && cItem.meta.itemForwarded == null && cItem.meta.itemDeleted == null && !cItem.meta.isLive) {
|
||||
@@ -582,7 +582,7 @@ fun ChatItemView(
|
||||
DeletedItemView(cItem, cInfo.timedMessagesTTL, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -631,14 +631,13 @@ fun ChatItemView(
|
||||
}
|
||||
|
||||
@Composable fun EventItemView() {
|
||||
val chatsCtx = if (LocalContentTag.current == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
CIEventView(eventItemViewText(reversedChatItems))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeletedItem() {
|
||||
MarkedDeletedItemView(cItem, cInfo, cInfo.timedMessagesTTL, revealed, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
MarkedDeletedItemView(chatsCtx, cItem, cInfo, cInfo.timedMessagesTTL, revealed, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
if (revealed.value) {
|
||||
HideItemAction(revealed, showMenu, reveal)
|
||||
@@ -648,7 +647,7 @@ fun ChatItemView(
|
||||
ExpandItemAction(revealed, showMenu, reveal)
|
||||
}
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
DeleteItemAction(cItem, revealed, showMenu, questionText = generalGetString(MR.strings.delete_message_cannot_be_undone_warning), deleteMessage, deleteMessages)
|
||||
DeleteItemAction(chatsCtx, cItem, revealed, showMenu, questionText = generalGetString(MR.strings.delete_message_cannot_be_undone_warning), deleteMessage, deleteMessages)
|
||||
if (cItem.canBeDeletedForSelf) {
|
||||
Divider()
|
||||
SelectItemAction(showMenu, selectChatItem)
|
||||
@@ -729,11 +728,11 @@ fun ChatItemView(
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeature -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndChatFeature -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatPreference -> {
|
||||
@@ -742,23 +741,23 @@ fun ChatItemView(
|
||||
DeleteItemMenu()
|
||||
}
|
||||
is CIContent.SndChatPreference -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeature -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndGroupFeature -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeatureRejected -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeatureRejected -> {
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(chatsCtx, cInfo, cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndModerated -> DeletedItem()
|
||||
@@ -830,6 +829,7 @@ fun ItemInfoAction(
|
||||
|
||||
@Composable
|
||||
fun DeleteItemAction(
|
||||
chatsCtx: ChatModel.ChatsContext,
|
||||
cItem: ChatItem,
|
||||
revealed: State<Boolean>,
|
||||
showMenu: MutableState<Boolean>,
|
||||
@@ -838,8 +838,6 @@ fun DeleteItemAction(
|
||||
deleteMessages: (List<Long>) -> Unit,
|
||||
buttonText: String = stringResource(MR.strings.delete_verb),
|
||||
) {
|
||||
val contentTag = LocalContentTag.current
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
ItemAction(
|
||||
buttonText,
|
||||
painterResource(MR.images.ic_delete),
|
||||
@@ -1424,6 +1422,7 @@ fun PreviewChatItemView(
|
||||
chatItem: ChatItem = ChatItem.getSampleData(1, CIDirection.DirectSnd(), Clock.System.now(), "hello")
|
||||
) {
|
||||
ChatItemView(
|
||||
chatsCtx = ChatModel.ChatsContext(contentTag = null),
|
||||
rhId = null,
|
||||
ChatInfo.Direct.sampleData,
|
||||
chatItem,
|
||||
@@ -1473,6 +1472,7 @@ fun PreviewChatItemView(
|
||||
fun PreviewChatItemViewDeletedContent() {
|
||||
SimpleXTheme {
|
||||
ChatItemView(
|
||||
chatsCtx = ChatModel.ChatsContext(contentTag = null),
|
||||
rhId = null,
|
||||
ChatInfo.Direct.sampleData,
|
||||
ChatItem.getDeletedContentSampleData(),
|
||||
|
||||
+3
-6
@@ -12,17 +12,15 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.chatModel
|
||||
import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
fun MarkedDeletedItemView(ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: Int?, revealed: State<Boolean>, showViaProxy: Boolean, showTimestamp: Boolean) {
|
||||
fun MarkedDeletedItemView(chatsCtx: ChatModel.ChatsContext, ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: Int?, revealed: State<Boolean>, showViaProxy: Boolean, showTimestamp: Boolean) {
|
||||
val sentColor = MaterialTheme.appColors.sentMessage
|
||||
val receivedColor = MaterialTheme.appColors.receivedMessage
|
||||
Surface(
|
||||
@@ -35,7 +33,7 @@ fun MarkedDeletedItemView(ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: In
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(Modifier.weight(1f, false)) {
|
||||
MergedMarkedDeletedText(ci, chatInfo, revealed)
|
||||
MergedMarkedDeletedText(chatsCtx, ci, chatInfo, revealed)
|
||||
}
|
||||
CIMetaView(ci, timedMessagesTTL, showViaProxy = showViaProxy, showTimestamp = showTimestamp)
|
||||
}
|
||||
@@ -43,8 +41,7 @@ fun MarkedDeletedItemView(ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: In
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MergedMarkedDeletedText(chatItem: ChatItem, chatInfo: ChatInfo, revealed: State<Boolean>) {
|
||||
val chatsCtx = if (LocalContentTag.current == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
private fun MergedMarkedDeletedText(chatsCtx: ChatModel.ChatsContext, chatItem: ChatItem, chatInfo: ChatInfo, revealed: State<Boolean>) {
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
var i = getChatItemIndexOrNull(chatItem, reversedChatItems)
|
||||
val ciCategory = chatItem.mergeCategory
|
||||
|
||||
+20
-20
@@ -189,7 +189,7 @@ fun ErrorChatListItem() {
|
||||
suspend fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) {
|
||||
when {
|
||||
contact.activeConn == null && contact.profile.contactLink != null && contact.active -> askCurrentOrIncognitoProfileConnectContactViaAddress(chatModel, rhId, contact, close = null, openChat = true)
|
||||
else -> openChat(rhId, ChatInfo.Direct(contact))
|
||||
else -> openDirectChat(rhId, contact.contactId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,30 +197,33 @@ suspend fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatMo
|
||||
when (groupInfo.membership.memberStatus) {
|
||||
GroupMemberStatus.MemInvited -> acceptGroupInvitationAlertDialog(rhId, groupInfo, chatModel, inProgress)
|
||||
GroupMemberStatus.MemAccepted -> groupInvitationAcceptedAlert(rhId)
|
||||
else -> openChat(rhId, ChatInfo.Group(groupInfo))
|
||||
else -> openGroupChat(rhId, groupInfo.groupId)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) = openChat(rhId, ChatInfo.Local(noteFolder))
|
||||
suspend fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) = openChat(secondaryChatsCtx = null, rhId, ChatInfo.Local(noteFolder))
|
||||
|
||||
suspend fun openDirectChat(rhId: Long?, contactId: Long) = openChat(rhId, ChatType.Direct, contactId)
|
||||
suspend fun openDirectChat(rhId: Long?, contactId: Long) = openChat(secondaryChatsCtx = null, rhId, ChatType.Direct, contactId)
|
||||
|
||||
suspend fun openGroupChat(rhId: Long?, groupId: Long, contentTag: MsgContentTag? = null) = openChat(rhId, ChatType.Group, groupId, contentTag)
|
||||
suspend fun openGroupChat(rhId: Long?, groupId: Long) = openChat(secondaryChatsCtx = null, rhId, ChatType.Group, groupId)
|
||||
|
||||
suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, contentTag: MsgContentTag? = null) = openChat(rhId, chatInfo.chatType, chatInfo.apiId, contentTag)
|
||||
suspend fun openChat(secondaryChatsCtx: ChatModel.ChatsContext?, rhId: Long?, chatInfo: ChatInfo) = openChat(secondaryChatsCtx, rhId, chatInfo.chatType, chatInfo.apiId)
|
||||
|
||||
suspend fun openChat(
|
||||
secondaryChatsCtx: ChatModel.ChatsContext?,
|
||||
rhId: Long?,
|
||||
chatType: ChatType,
|
||||
apiId: Long,
|
||||
contentTag: MsgContentTag? = null,
|
||||
openAroundItemId: Long? = null
|
||||
) =
|
||||
) {
|
||||
if (secondaryChatsCtx != null) {
|
||||
chatModel.secondaryChatsContext.value = secondaryChatsCtx
|
||||
}
|
||||
apiLoadMessages(
|
||||
chatsCtx = secondaryChatsCtx ?: chatModel.chatsContext,
|
||||
rhId,
|
||||
chatType,
|
||||
apiId,
|
||||
contentTag,
|
||||
if (openAroundItemId != null) {
|
||||
ChatPagination.Around(openAroundItemId, ChatPagination.INITIAL_COUNT)
|
||||
} else {
|
||||
@@ -229,23 +232,22 @@ suspend fun openChat(
|
||||
"",
|
||||
openAroundItemId
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun openLoadedChat(chat: Chat, contentTag: MsgContentTag? = null) {
|
||||
suspend fun openLoadedChat(chat: Chat) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
chatsCtx.chatItemStatuses.clear()
|
||||
chatsCtx.chatItems.replaceAll(chat.chatItems)
|
||||
chatModel.chatsContext.chatItemStatuses.clear()
|
||||
chatModel.chatsContext.chatItems.replaceAll(chat.chatItems)
|
||||
chatModel.chatId.value = chat.chatInfo.id
|
||||
chatsCtx.chatState.clear()
|
||||
chatModel.chatsContext.chatState.clear()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiFindMessages(ch: Chat, search: String, contentTag: MsgContentTag?) {
|
||||
suspend fun apiFindMessages(chatsCtx: ChatModel.ChatsContext, ch: Chat, search: String) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
chatsCtx.chatItems.clearAndNotify()
|
||||
}
|
||||
apiLoadMessages(ch.remoteHostId, ch.chatInfo.chatType, ch.chatInfo.apiId, contentTag, pagination = if (search.isNotEmpty()) ChatPagination.Last(ChatPagination.INITIAL_COUNT) else ChatPagination.Initial(ChatPagination.INITIAL_COUNT), search = search)
|
||||
apiLoadMessages(chatsCtx, ch.remoteHostId, ch.chatInfo.chatType, ch.chatInfo.apiId, pagination = if (search.isNotEmpty()) ChatPagination.Last(ChatPagination.INITIAL_COUNT) else ChatPagination.Initial(ChatPagination.INITIAL_COUNT), search = search)
|
||||
}
|
||||
|
||||
suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) = coroutineScope {
|
||||
@@ -608,9 +610,7 @@ fun markChatRead(c: Chat) {
|
||||
chatModel.chatsContext.markChatItemsRead(chat.remoteHostId, chat.chatInfo.id)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.markChatItemsRead(chat.remoteHostId, chat.chatInfo.id)
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.markChatItemsRead(chat.remoteHostId, chat.chatInfo.id)
|
||||
}
|
||||
chatModel.controller.apiChatRead(
|
||||
chat.remoteHostId,
|
||||
|
||||
+2
-2
@@ -55,13 +55,13 @@ fun ContactListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>, showDel
|
||||
when (contactType) {
|
||||
ContactType.RECENT -> {
|
||||
withApi {
|
||||
openChat(rhId, chat.chatInfo)
|
||||
openChat(secondaryChatsCtx = null, rhId, chat.chatInfo)
|
||||
ModalManager.start.closeModals()
|
||||
}
|
||||
}
|
||||
ContactType.CHAT_DELETED -> {
|
||||
withApi {
|
||||
openChat(rhId, chat.chatInfo)
|
||||
openChat(secondaryChatsCtx = null, rhId, chat.chatInfo)
|
||||
ModalManager.start.closeModals()
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -543,11 +543,9 @@ fun deleteChatDatabaseFilesAndState() {
|
||||
chatModel.chatsContext.popChatCollector.clear()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.chatItems.clearAndNotify()
|
||||
chatModel.secondaryChatsContext.chats.clear()
|
||||
chatModel.secondaryChatsContext.popChatCollector.clear()
|
||||
}
|
||||
chatModel.secondaryChatsContext.value?.chatItems?.clearAndNotify()
|
||||
chatModel.secondaryChatsContext.value?.chats?.clear()
|
||||
chatModel.secondaryChatsContext.value?.popChatCollector?.clear()
|
||||
}
|
||||
}
|
||||
chatModel.users.clear()
|
||||
|
||||
+9
-2
@@ -161,13 +161,20 @@ class ModalManager(private val placement: ModalPlacement? = null) {
|
||||
|
||||
fun closeModal() {
|
||||
if (modalViews.isNotEmpty()) {
|
||||
if (modalViews.lastOrNull()?.animated == false) modalViews.removeAt(modalViews.lastIndex)
|
||||
else runAtomically { toRemove.add(modalViews.lastIndex - min(toRemove.size, modalViews.lastIndex)) }
|
||||
val lastModal = modalViews.lastOrNull()
|
||||
if (lastModal != null) {
|
||||
if (lastModal.id == ModalViewId.SECONDARY_CHAT) chatModel.secondaryChatsContext.value = null
|
||||
if (!lastModal.animated)
|
||||
modalViews.removeAt(modalViews.lastIndex)
|
||||
else
|
||||
runAtomically { toRemove.add(modalViews.lastIndex - min(toRemove.size, modalViews.lastIndex)) }
|
||||
}
|
||||
}
|
||||
_modalCount.value = modalViews.size - toRemove.size
|
||||
}
|
||||
|
||||
fun closeModals() {
|
||||
chatModel.secondaryChatsContext.value = null
|
||||
modalViews.clear()
|
||||
toRemove.clear()
|
||||
_modalCount.value = 0
|
||||
|
||||
@@ -61,9 +61,7 @@ fun showApp() {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
chatModel.secondaryChatsContext.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ private fun ActiveCallInteractiveAreaOneHand(call: Call, showMenu: MutableState<
|
||||
val chat = chatModel.getChat(call.contact.id)
|
||||
if (chat != null) {
|
||||
withBGApi {
|
||||
openChat(chat.remoteHostId, chat.chatInfo)
|
||||
openChat(secondaryChatsCtx = null, chat.remoteHostId, chat.chatInfo)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -116,7 +116,7 @@ private fun ActiveCallInteractiveAreaNonOneHand(call: Call, showMenu: MutableSta
|
||||
val chat = chatModel.getChat(call.contact.id)
|
||||
if (chat != null) {
|
||||
withBGApi {
|
||||
openChat(chat.remoteHostId, chat.chatInfo)
|
||||
openChat(secondaryChatsCtx = null, chat.remoteHostId, chat.chatInfo)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user