mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 20:29:53 +00:00
kotlin: refactor chat contexts 1 (remove functions creating indirection) (#5827)
* kotlin: refactor chat contexts 1 * remove withChats * comment * remove withReportChatsIfOpen * remove comment * fix desktop
This commit is contained in:
parent
14d9240995
commit
38c2529d8b
33 changed files with 598 additions and 580 deletions
|
@ -24,7 +24,6 @@ import chat.simplex.app.views.call.CallActivity
|
|||
import chat.simplex.common.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.call.*
|
||||
|
@ -33,7 +32,6 @@ import chat.simplex.common.views.helpers.*
|
|||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -94,7 +92,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
|||
Lifecycle.Event.ON_START -> {
|
||||
isAppOnForeground = true
|
||||
if (chatModel.chatRunning.value == true) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
kotlin.runCatching {
|
||||
val currentUserId = chatModel.currentUser.value?.userId
|
||||
val chats = ArrayList(chatController.apiGetChats(chatModel.remoteHostId()))
|
||||
|
@ -107,7 +105,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
|||
/** Pass old chatStats because unreadCounter can be changed already while [ChatController.apiGetChats] is executing */
|
||||
if (indexOfCurrentChat >= 0) chats[indexOfCurrentChat] = chats[indexOfCurrentChat].copy(chatStats = oldStats)
|
||||
}
|
||||
updateChats(chats)
|
||||
chatModel.chatsContext.updateChats(chats)
|
||||
}
|
||||
}.onFailure { Log.e(TAG, it.stackTraceToString()) }
|
||||
}
|
||||
|
|
|
@ -14,12 +14,11 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import chat.simplex.common.AppScreen
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.clear
|
||||
import chat.simplex.common.model.clearAndNotify
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import androidx.compose.ui.platform.LocalContext as LocalContext1
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
actual fun showToast(text: String, timeout: Long) = Toast.makeText(androidAppContext, text, Toast.LENGTH_SHORT).show()
|
||||
|
||||
|
@ -76,13 +75,13 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
|
|||
ModalManager.start.closeModal()
|
||||
} else if (chatModel.chatId.value != null) {
|
||||
withApi {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
// Since no modals are open, the problem is probably in ChatView
|
||||
chatModel.chatId.value = null
|
||||
chatItems.clearAndNotify()
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -67,7 +67,7 @@ object ChatModel {
|
|||
val chatId = mutableStateOf<String?>(null)
|
||||
val openAroundItemId: MutableState<Long?> = mutableStateOf(null)
|
||||
val chatsContext = ChatsContext(null)
|
||||
val reportsChatsContext = ChatsContext(MsgContentTag.Report)
|
||||
val secondaryChatsContext = ChatsContext(MsgContentTag.Report)
|
||||
// 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
|
||||
|
@ -170,36 +170,6 @@ object ChatModel {
|
|||
// return true if you handled the click
|
||||
var centerPanelBackgroundClickHandler: (() -> Boolean)? = null
|
||||
|
||||
fun chatsForContent(contentTag: MsgContentTag?): State<SnapshotStateList<Chat>> = when(contentTag) {
|
||||
null -> chatsContext.chats
|
||||
MsgContentTag.Report -> reportsChatsContext.chats
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
fun chatItemsForContent(contentTag: MsgContentTag?): State<SnapshotStateList<ChatItem>> = when(contentTag) {
|
||||
null -> chatsContext.chatItems
|
||||
MsgContentTag.Report -> reportsChatsContext.chatItems
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
fun chatStateForContent(contentTag: MsgContentTag?): ActiveChatState = when(contentTag) {
|
||||
null -> chatsContext.chatState
|
||||
MsgContentTag.Report -> reportsChatsContext.chatState
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
fun chatItemsChangesListenerForContent(contentTag: MsgContentTag?): ChatItemsChangesListener? = when(contentTag) {
|
||||
null -> chatsContext.chatItemsChangesListener
|
||||
MsgContentTag.Report -> reportsChatsContext.chatItemsChangesListener
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
fun setChatItemsChangeListenerForContent(listener: ChatItemsChangesListener?, contentTag: MsgContentTag?) = when(contentTag) {
|
||||
null -> chatsContext.chatItemsChangesListener = listener
|
||||
MsgContentTag.Report -> reportsChatsContext.chatItemsChangesListener = listener
|
||||
else -> TODO()
|
||||
}
|
||||
|
||||
fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) {
|
||||
currentUser.value
|
||||
} else {
|
||||
|
@ -324,21 +294,6 @@ object ChatModel {
|
|||
}
|
||||
}
|
||||
|
||||
// running everything inside the block on main thread. Make sure any heavy computation is moved to a background thread
|
||||
suspend fun <T> withChats(contentTag: MsgContentTag? = null, action: suspend ChatsContext.() -> T): T = withContext(Dispatchers.Main) {
|
||||
when {
|
||||
contentTag == null -> chatsContext.action()
|
||||
contentTag == MsgContentTag.Report -> reportsChatsContext.action()
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T> withReportsChatsIfOpen(action: suspend ChatsContext.() -> T) = withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) {
|
||||
reportsChatsContext.action()
|
||||
}
|
||||
}
|
||||
|
||||
class ChatsContext(private 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.
|
||||
|
@ -659,8 +614,9 @@ object ChatModel {
|
|||
subject
|
||||
.throttleLatest(2000)
|
||||
.collect {
|
||||
withChats(contentTag) {
|
||||
chats.replaceAll(popCollectedChats())
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = if (contentTag == null) chatsContext else secondaryChatsContext
|
||||
chatsCtx.chats.replaceAll(popCollectedChats())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -960,17 +916,17 @@ object ChatModel {
|
|||
|
||||
suspend fun addLiveDummy(chatInfo: ChatInfo): ChatItem {
|
||||
val cItem = ChatItem.liveDummy(chatInfo is ChatInfo.Direct)
|
||||
withChats {
|
||||
chatItems.addAndNotify(cItem, contentTag = null)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsContext.chatItems.addAndNotify(cItem, contentTag = null)
|
||||
}
|
||||
return cItem
|
||||
}
|
||||
|
||||
fun removeLiveDummy() {
|
||||
if (chatItemsForContent(null).value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
if (chatsContext.chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
withApi {
|
||||
withChats {
|
||||
chatItems.removeLastAndNotify(contentTag = null)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsContext.chatItems.removeLastAndNotify(contentTag = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1042,9 +998,11 @@ object ChatModel {
|
|||
fun replaceConnReqView(id: String, withId: String) {
|
||||
if (id == showingInvitation.value?.connId) {
|
||||
withApi {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
showingInvitation.value = null
|
||||
chatItems.clearAndNotify()
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
@ -1055,9 +1013,10 @@ object ChatModel {
|
|||
|
||||
fun dismissConnReqView(id: String) = withApi {
|
||||
if (id == showingInvitation.value?.connId) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
showingInvitation.value = null
|
||||
chatItems.clearAndNotify()
|
||||
// TODO [contexts] see replaceConnReqView
|
||||
chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatId.value = null
|
||||
}
|
||||
// Close NewChatView
|
||||
|
@ -2739,7 +2698,8 @@ fun MutableState<SnapshotStateList<Chat>>.add(index: Int, elem: Chat) {
|
|||
}
|
||||
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.addAndNotify(index: Int, elem: ChatItem, contentTag: MsgContentTag?) {
|
||||
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(index, elem); chatModel.chatItemsChangesListenerForContent(contentTag)?.added(elem.id to elem.isRcvNew, index) }
|
||||
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) {
|
||||
|
@ -2751,7 +2711,8 @@ fun <T> MutableList<T>.removeAll(predicate: (T) -> Boolean): Boolean = if (isEmp
|
|||
|
||||
// Adds item to chatItems and notifies a listener about newly added item
|
||||
fun MutableState<SnapshotStateList<ChatItem>>.addAndNotify(elem: ChatItem, contentTag: MsgContentTag?) {
|
||||
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(elem); chatModel.chatItemsChangesListenerForContent(contentTag)?.added(elem.id to elem.isRcvNew, lastIndex) }
|
||||
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>) {
|
||||
|
@ -2781,7 +2742,7 @@ fun MutableState<SnapshotStateList<ChatItem>>.removeAllAndNotify(block: (ChatIte
|
|||
}
|
||||
if (toRemove.isNotEmpty()) {
|
||||
chatModel.chatsContext.chatItemsChangesListener?.removed(toRemove, value)
|
||||
chatModel.reportsChatsContext.chatItemsChangesListener?.removed(toRemove, value)
|
||||
chatModel.secondaryChatsContext.chatItemsChangesListener?.removed(toRemove, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2801,7 +2762,8 @@ fun MutableState<SnapshotStateList<ChatItem>>.removeLastAndNotify(contentTag: Ms
|
|||
val rem = removeLast()
|
||||
removed = Triple(rem.id, remIndex, rem.isRcvNew)
|
||||
}
|
||||
chatModel.chatItemsChangesListenerForContent(contentTag)?.removed(listOf(removed), value)
|
||||
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>) {
|
||||
|
@ -2816,7 +2778,7 @@ fun MutableState<SnapshotStateList<Chat>>.clear() {
|
|||
fun MutableState<SnapshotStateList<ChatItem>>.clearAndNotify() {
|
||||
value = SnapshotStateList()
|
||||
chatModel.chatsContext.chatItemsChangesListener?.cleared()
|
||||
chatModel.reportsChatsContext.chatItemsChangesListener?.cleared()
|
||||
chatModel.secondaryChatsContext.chatItemsChangesListener?.cleared()
|
||||
}
|
||||
|
||||
fun <T> State<SnapshotStateList<T>>.asReversed(): MutableList<T> = value.asReversed()
|
||||
|
|
|
@ -17,8 +17,6 @@ import androidx.compose.ui.unit.dp
|
|||
import chat.simplex.common.model.ChatController.getNetCfg
|
||||
import chat.simplex.common.model.ChatController.setNetCfg
|
||||
import chat.simplex.common.model.ChatModel.changingActiveUserMutex
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.model.MsgContent.MCUnknown
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import chat.simplex.common.platform.*
|
||||
|
@ -544,9 +542,9 @@ object ChatController {
|
|||
}
|
||||
Log.d(TAG, "startChat: started")
|
||||
} else {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chats = apiGetChats(null)
|
||||
updateChats(chats)
|
||||
chatModel.chatsContext.updateChats(chats)
|
||||
}
|
||||
Log.d(TAG, "startChat: running")
|
||||
}
|
||||
|
@ -627,9 +625,9 @@ object ChatController {
|
|||
val hasUser = chatModel.currentUser.value != null
|
||||
chatModel.userAddress.value = if (hasUser) apiGetUserAddress(rhId) else null
|
||||
chatModel.chatItemTTL.value = if (hasUser) getChatItemTTL(rhId) else ChatItemTTL.None
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chats = apiGetChats(rhId)
|
||||
updateChats(chats)
|
||||
chatModel.chatsContext.updateChats(chats)
|
||||
}
|
||||
chatModel.userTags.value = apiGetChatTags(rhId).takeIf { hasUser } ?: emptyList()
|
||||
chatModel.activeChatTagFilter.value = null
|
||||
|
@ -1490,8 +1488,8 @@ object ChatController {
|
|||
suspend fun deleteChat(chat: Chat, chatDeleteMode: ChatDeleteMode = ChatDeleteMode.Full(notify = true)) {
|
||||
val cInfo = chat.chatInfo
|
||||
if (apiDeleteChat(rh = chat.remoteHostId, type = cInfo.chatType, id = cInfo.apiId, chatDeleteMode = chatDeleteMode)) {
|
||||
withChats {
|
||||
removeChat(chat.remoteHostId, cInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(chat.remoteHostId, cInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1539,11 +1537,11 @@ object ChatController {
|
|||
withBGApi {
|
||||
val updatedChatInfo = apiClearChat(chat.remoteHostId, chat.chatInfo.chatType, chat.chatInfo.apiId)
|
||||
if (updatedChatInfo != null) {
|
||||
withChats {
|
||||
clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
}
|
||||
withChats(MsgContentTag.Report) {
|
||||
clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.secondaryChatsContext.clearChat(chat.remoteHostId, updatedChatInfo)
|
||||
}
|
||||
ntfManager.cancelNotificationsForChat(chat.chatInfo.id)
|
||||
close?.invoke()
|
||||
|
@ -1975,12 +1973,14 @@ object ChatController {
|
|||
val r = sendCmd(rh, CC.ApiJoinGroup(groupId))
|
||||
when (r) {
|
||||
is CR.UserAcceptedGroupSent ->
|
||||
withChats {
|
||||
updateGroup(rh, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rh, r.groupInfo)
|
||||
}
|
||||
is CR.ChatCmdError -> {
|
||||
val e = r.chatError
|
||||
suspend fun deleteGroup() { if (apiDeleteChat(rh, ChatType.Group, groupId)) { withChats { removeChat(rh, "#$groupId") } } }
|
||||
suspend fun deleteGroup() { if (apiDeleteChat(rh, ChatType.Group, groupId)) {
|
||||
withContext(Dispatchers.Main) { chatModel.chatsContext.removeChat(rh, "#$groupId") } }
|
||||
}
|
||||
if (e is ChatError.ChatErrorAgent && e.agentError is AgentErrorType.SMP && e.agentError.smpErr is SMPErrorType.AUTH) {
|
||||
deleteGroup()
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.alert_title_group_invitation_expired), generalGetString(MR.strings.alert_message_group_invitation_expired))
|
||||
|
@ -2134,8 +2134,8 @@ object ChatController {
|
|||
val prefs = contact.mergedPreferences.toPreferences().setAllowed(feature, param = param)
|
||||
val toContact = apiSetContactPrefs(rh, contact.contactId, prefs)
|
||||
if (toContact != null) {
|
||||
withChats {
|
||||
updateContact(rh, toContact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rh, toContact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2406,19 +2406,19 @@ object ChatController {
|
|||
when (r) {
|
||||
is CR.ContactDeletedByContact -> {
|
||||
if (active(r.user) && r.contact.directOrUsed) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.ContactConnected -> {
|
||||
if (active(r.user) && r.contact.directOrUsed) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
val conn = r.contact.activeConn
|
||||
if (conn != null) {
|
||||
chatModel.replaceConnReqView(conn.id, "@${r.contact.contactId}")
|
||||
removeChat(rhId, conn.id)
|
||||
chatModel.chatsContext.removeChat(rhId, conn.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2429,24 +2429,24 @@ object ChatController {
|
|||
}
|
||||
is CR.ContactConnecting -> {
|
||||
if (active(r.user) && r.contact.directOrUsed) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
val conn = r.contact.activeConn
|
||||
if (conn != null) {
|
||||
chatModel.replaceConnReqView(conn.id, "@${r.contact.contactId}")
|
||||
removeChat(rhId, conn.id)
|
||||
chatModel.chatsContext.removeChat(rhId, conn.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.ContactSndReady -> {
|
||||
if (active(r.user) && r.contact.directOrUsed) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
val conn = r.contact.activeConn
|
||||
if (conn != null) {
|
||||
chatModel.replaceConnReqView(conn.id, "@${r.contact.contactId}")
|
||||
removeChat(rhId, conn.id)
|
||||
chatModel.chatsContext.removeChat(rhId, conn.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2456,11 +2456,11 @@ object ChatController {
|
|||
val contactRequest = r.contactRequest
|
||||
val cInfo = ChatInfo.ContactRequest(contactRequest)
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
if (hasChat(rhId, contactRequest.id)) {
|
||||
updateChatInfo(rhId, cInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (chatModel.chatsContext.hasChat(rhId, contactRequest.id)) {
|
||||
chatModel.chatsContext.updateChatInfo(rhId, cInfo)
|
||||
} else {
|
||||
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = listOf()))
|
||||
chatModel.chatsContext.addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = listOf()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2469,18 +2469,20 @@ object ChatController {
|
|||
is CR.ContactUpdated -> {
|
||||
if (active(r.user) && chatModel.chatsContext.hasChat(rhId, r.toContact.id)) {
|
||||
val cInfo = ChatInfo.Direct(r.toContact)
|
||||
withChats {
|
||||
updateChatInfo(rhId, cInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatInfo(rhId, cInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.GroupMemberUpdated -> {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.toMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2489,8 +2491,8 @@ object ChatController {
|
|||
if (chatModel.chatId.value == r.mergedContact.id) {
|
||||
chatModel.chatId.value = r.intoContact.id
|
||||
}
|
||||
withChats {
|
||||
removeChat(rhId, r.mergedContact.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, r.mergedContact.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2501,8 +2503,8 @@ object ChatController {
|
|||
is CR.ContactSubSummary -> {
|
||||
for (sub in r.contactSubscriptions) {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContact(rhId, sub.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, sub.contact)
|
||||
}
|
||||
}
|
||||
val err = sub.contactError
|
||||
|
@ -2528,20 +2530,22 @@ object ChatController {
|
|||
val cInfo = chatItem.chatInfo
|
||||
val cItem = chatItem.chatItem
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
addChatItem(rhId, cInfo, cItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.addChatItem(rhId, cInfo, cItem)
|
||||
if (cItem.isActiveReport) {
|
||||
increaseGroupReportsCounter(rhId, cInfo.id)
|
||||
chatModel.chatsContext.increaseGroupReportsCounter(rhId, cInfo.id)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.isReport) {
|
||||
addChatItem(rhId, cInfo, cItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.addChatItem(rhId, cInfo, cItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (cItem.isRcvNew && cInfo.ntfsEnabled(cItem)) {
|
||||
withChats {
|
||||
increaseUnreadCounter(rhId, r.user)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.increaseUnreadCounter(rhId, r.user)
|
||||
}
|
||||
}
|
||||
val file = cItem.file
|
||||
|
@ -2562,12 +2566,14 @@ object ChatController {
|
|||
val cInfo = chatItem.chatInfo
|
||||
val cItem = chatItem.chatItem
|
||||
if (!cItem.isDeletedContent && active(r.user)) {
|
||||
withChats {
|
||||
updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatItem(cInfo, cItem, status = cItem.meta.itemStatus)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.isReport) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2576,12 +2582,14 @@ object ChatController {
|
|||
chatItemUpdateNotify(rhId, r.user, r.chatItem)
|
||||
is CR.ChatItemReaction -> {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (r.reaction.chatReaction.chatItem.isReport) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2590,8 +2598,8 @@ object ChatController {
|
|||
if (!active(r.user)) {
|
||||
r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) ->
|
||||
if (toChatItem == null && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled(deletedChatItem.chatItem)) {
|
||||
withChats {
|
||||
decreaseUnreadCounter(rhId, r.user)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.decreaseUnreadCounter(rhId, r.user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2614,22 +2622,24 @@ object ChatController {
|
|||
generalGetString(if (toChatItem != null) MR.strings.marked_deleted_description else MR.strings.deleted_description)
|
||||
)
|
||||
}
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (toChatItem == null) {
|
||||
removeChatItem(rhId, cInfo, cItem)
|
||||
chatModel.chatsContext.removeChatItem(rhId, cInfo, cItem)
|
||||
} else {
|
||||
upsertChatItem(rhId, cInfo, toChatItem.chatItem)
|
||||
chatModel.chatsContext.upsertChatItem(rhId, cInfo, toChatItem.chatItem)
|
||||
}
|
||||
if (cItem.isActiveReport) {
|
||||
decreaseGroupReportsCounter(rhId, cInfo.id)
|
||||
chatModel.chatsContext.decreaseGroupReportsCounter(rhId, cInfo.id)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.isReport) {
|
||||
if (toChatItem == null) {
|
||||
removeChatItem(rhId, cInfo, cItem)
|
||||
} else {
|
||||
upsertChatItem(rhId, cInfo, toChatItem.chatItem)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2640,9 +2650,9 @@ object ChatController {
|
|||
}
|
||||
is CR.ReceivedGroupInvitation -> {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
// update so that repeat group invitations are not duplicated
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
}
|
||||
// TODO NtfManager.shared.notifyGroupInvitation
|
||||
}
|
||||
|
@ -2650,137 +2660,149 @@ object ChatController {
|
|||
is CR.UserAcceptedGroupSent -> {
|
||||
if (!active(r.user)) return
|
||||
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
val conn = r.hostContact?.activeConn
|
||||
if (conn != null) {
|
||||
chatModel.replaceConnReqView(conn.id, "#${r.groupInfo.groupId}")
|
||||
removeChat(rhId, conn.id)
|
||||
chatModel.chatsContext.removeChat(rhId, conn.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.GroupLinkConnecting -> {
|
||||
if (!active(r.user)) return
|
||||
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
val hostConn = r.hostMember.activeConn
|
||||
if (hostConn != null) {
|
||||
chatModel.replaceConnReqView(hostConn.id, "#${r.groupInfo.groupId}")
|
||||
removeChat(rhId, hostConn.id)
|
||||
chatModel.chatsContext.removeChat(rhId, hostConn.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.BusinessLinkConnecting -> {
|
||||
if (!active(r.user)) return
|
||||
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
}
|
||||
if (chatModel.chatId.value == r.fromContact.id) {
|
||||
openGroupChat(rhId, r.groupInfo.groupId)
|
||||
}
|
||||
withChats {
|
||||
removeChat(rhId, r.fromContact.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, r.fromContact.id)
|
||||
}
|
||||
}
|
||||
is CR.JoinedGroupMemberConnecting ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
is CR.DeletedMemberUser -> // TODO update user member
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
if (r.withMessages) {
|
||||
removeMemberItems(rhId, r.groupInfo.membership, byMember = r.member, r.groupInfo)
|
||||
chatModel.chatsContext.removeMemberItems(rhId, r.groupInfo.membership, byMember = r.member, r.groupInfo)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (r.withMessages) {
|
||||
removeMemberItems(rhId, r.groupInfo.membership, byMember = r.member, r.groupInfo)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.DeletedMember ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
if (r.withMessages) {
|
||||
removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
chatModel.chatsContext.removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.deletedMember)
|
||||
if (r.withMessages) {
|
||||
removeMemberItems(rhId, r.deletedMember, byMember = r.byMember, r.groupInfo)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.LeftMember ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.MemberRole ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.MembersRoleUser ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
r.members.forEach { member ->
|
||||
upsertGroupMember(rhId, r.groupInfo, member)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, member)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
r.members.forEach { member ->
|
||||
upsertGroupMember(rhId, r.groupInfo, member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
r.members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.MemberBlockedForAll ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.GroupDeleted -> // TODO update user member
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
}
|
||||
}
|
||||
is CR.UserJoinedGroup ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroup(rhId, r.groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.groupInfo)
|
||||
}
|
||||
}
|
||||
is CR.JoinedGroupMember ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
is CR.ConnectedToGroupMember -> {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, r.groupInfo, r.member)
|
||||
}
|
||||
}
|
||||
if (r.memberContact != null) {
|
||||
|
@ -2789,14 +2811,14 @@ object ChatController {
|
|||
}
|
||||
is CR.GroupUpdated ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroup(rhId, r.toGroup)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, r.toGroup)
|
||||
}
|
||||
}
|
||||
is CR.NewMemberContactReceivedInv ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
}
|
||||
}
|
||||
is CR.RcvFileStart ->
|
||||
|
@ -2897,26 +2919,26 @@ object ChatController {
|
|||
}
|
||||
is CR.ContactSwitch ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContactConnectionStats(rhId, r.contact, r.switchProgress.connectionStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(rhId, r.contact, r.switchProgress.connectionStats)
|
||||
}
|
||||
}
|
||||
is CR.GroupMemberSwitch ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.switchProgress.connectionStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.switchProgress.connectionStats)
|
||||
}
|
||||
}
|
||||
is CR.ContactRatchetSync ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContactConnectionStats(rhId, r.contact, r.ratchetSyncProgress.connectionStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(rhId, r.contact, r.ratchetSyncProgress.connectionStats)
|
||||
}
|
||||
}
|
||||
is CR.GroupMemberRatchetSync ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats)
|
||||
}
|
||||
}
|
||||
is CR.RemoteHostSessionCode -> {
|
||||
|
@ -2930,8 +2952,8 @@ object ChatController {
|
|||
}
|
||||
is CR.ContactDisabled -> {
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3055,8 +3077,8 @@ object ChatController {
|
|||
}
|
||||
is CR.ContactPQEnabled ->
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
updateContact(rhId, r.contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, r.contact)
|
||||
}
|
||||
}
|
||||
is CR.ChatRespError -> when {
|
||||
|
@ -3120,8 +3142,8 @@ object ChatController {
|
|||
suspend fun leaveGroup(rh: Long?, groupId: Long) {
|
||||
val groupInfo = apiLeaveGroup(rh, groupId)
|
||||
if (groupInfo != null) {
|
||||
withChats {
|
||||
updateGroup(rh, groupInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rh, groupInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3130,10 +3152,12 @@ object ChatController {
|
|||
if (activeUser(rh, user)) {
|
||||
val cInfo = aChatItem.chatInfo
|
||||
val cItem = aChatItem.chatItem
|
||||
withChats { upsertChatItem(rh, cInfo, cItem) }
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.isReport) {
|
||||
upsertChatItem(rh, cInfo, cItem)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3147,15 +3171,16 @@ object ChatController {
|
|||
return
|
||||
}
|
||||
val cInfo = ChatInfo.Group(r.groupInfo)
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = chatModel.chatsContext
|
||||
r.chatItemIDs.forEach { itemId ->
|
||||
decreaseGroupReportsCounter(rhId, cInfo.id)
|
||||
val cItem = chatItems.value.lastOrNull { it.id == itemId } ?: return@forEach
|
||||
chatsCtx.decreaseGroupReportsCounter(rhId, cInfo.id)
|
||||
val cItem = chatsCtx.chatItems.value.lastOrNull { it.id == itemId } ?: return@forEach
|
||||
if (chatModel.chatId.value != null) {
|
||||
// Stop voice playback only inside a chat, allow to play in a chat list
|
||||
AudioPlayer.stop(cItem)
|
||||
}
|
||||
val isLastChatItem = getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id
|
||||
val isLastChatItem = chatsCtx.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id
|
||||
if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) {
|
||||
ntfManager.cancelNotificationsForChat(cInfo.id)
|
||||
ntfManager.displayNotification(
|
||||
|
@ -3170,22 +3195,25 @@ object ChatController {
|
|||
} else {
|
||||
CIDeleted.Deleted(Clock.System.now())
|
||||
}
|
||||
upsertChatItem(rhId, cInfo, cItem.copy(meta = cItem.meta.copy(itemDeleted = deleted)))
|
||||
chatsCtx.upsertChatItem(rhId, cInfo, cItem.copy(meta = cItem.meta.copy(itemDeleted = deleted)))
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
r.chatItemIDs.forEach { itemId ->
|
||||
val cItem = chatItems.value.lastOrNull { it.id == itemId } ?: return@forEach
|
||||
if (chatModel.chatId.value != null) {
|
||||
// Stop voice playback only inside a chat, allow to play in a chat list
|
||||
AudioPlayer.stop(cItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
val chatsCtx = chatModel.secondaryChatsContext
|
||||
r.chatItemIDs.forEach { itemId ->
|
||||
val cItem = chatsCtx.chatItems.value.lastOrNull { it.id == itemId } ?: return@forEach
|
||||
if (chatModel.chatId.value != null) {
|
||||
// Stop voice playback only inside a chat, allow to play in a chat list
|
||||
AudioPlayer.stop(cItem)
|
||||
}
|
||||
val deleted = if (r.member_ != null && (cItem.chatDir as CIDirection.GroupRcv?)?.groupMember?.groupMemberId != r.member_.groupMemberId) {
|
||||
CIDeleted.Moderated(Clock.System.now(), r.member_)
|
||||
} else {
|
||||
CIDeleted.Deleted(Clock.System.now())
|
||||
}
|
||||
chatsCtx.upsertChatItem(rhId, cInfo, cItem.copy(meta = cItem.meta.copy(itemDeleted = deleted)))
|
||||
}
|
||||
val deleted = if (r.member_ != null && (cItem.chatDir as CIDirection.GroupRcv?)?.groupMember?.groupMemberId != r.member_.groupMemberId) {
|
||||
CIDeleted.Moderated(Clock.System.now(), r.member_)
|
||||
} else {
|
||||
CIDeleted.Deleted(Clock.System.now())
|
||||
}
|
||||
upsertChatItem(rhId, cInfo, cItem.copy(meta = cItem.meta.copy(itemDeleted = deleted)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3197,8 +3225,14 @@ object ChatController {
|
|||
if (!activeUser(rh, user)) {
|
||||
notify()
|
||||
} else {
|
||||
val createdChat = withChats { upsertChatItem(rh, cInfo, cItem) }
|
||||
withReportsChatsIfOpen { if (cItem.content.msgContent is MsgContent.MCReport) { upsertChatItem(rh, cInfo, cItem) } }
|
||||
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 (createdChat) {
|
||||
notify()
|
||||
} else if (cItem.content is CIContent.RcvCall && cItem.content.status == CICallStatus.Missed) {
|
||||
|
@ -3243,15 +3277,17 @@ object ChatController {
|
|||
chatModel.users.addAll(users)
|
||||
chatModel.currentUser.value = user
|
||||
if (user == null) {
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
chats.clear()
|
||||
popChatCollector.clear()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatsContext.chats.clear()
|
||||
chatModel.chatsContext.popChatCollector.clear()
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
chatItems.clearAndNotify()
|
||||
chats.clear()
|
||||
popChatCollector.clear()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.chatItems.clearAndNotify()
|
||||
chatModel.secondaryChatsContext.chats.clear()
|
||||
chatModel.secondaryChatsContext.popChatCollector.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
val statuses = apiGetNetworkStatuses(rhId)
|
||||
|
|
|
@ -19,7 +19,6 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.*
|
||||
|
@ -37,17 +36,16 @@ import androidx.compose.ui.unit.*
|
|||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chat.group.ChatTTLSection
|
||||
import chat.simplex.common.views.chat.group.ProgressIndicator
|
||||
import chat.simplex.common.views.chatlist.updateChatSettings
|
||||
import chat.simplex.common.views.database.*
|
||||
import chat.simplex.common.views.newchat.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -126,8 +124,8 @@ fun ChatInfoView(
|
|||
val cStats = chatModel.controller.apiSwitchContact(chatRh, contact.contactId)
|
||||
connStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, cStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(chatRh, contact, cStats)
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
|
@ -140,8 +138,8 @@ fun ChatInfoView(
|
|||
val cStats = chatModel.controller.apiAbortSwitchContact(chatRh, contact.contactId)
|
||||
connStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, cStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(chatRh, contact, cStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,8 +169,8 @@ fun ChatInfoView(
|
|||
verify = { code ->
|
||||
chatModel.controller.apiVerifyContact(chatRh, ct.contactId, code)?.let { r ->
|
||||
val (verified, existingCode) = r
|
||||
withChats {
|
||||
updateContact(
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(
|
||||
chatRh,
|
||||
ct.copy(
|
||||
activeConn = ct.activeConn?.copy(
|
||||
|
@ -200,8 +198,8 @@ suspend fun syncContactConnection(rhId: Long?, contact: Contact, connectionStats
|
|||
val cStats = chatModel.controller.apiSyncContactRatchet(rhId, contact.contactId, force = force)
|
||||
connectionStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(rhId, contact, cStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(rhId, contact, cStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,14 +473,14 @@ fun deleteContact(chat: Chat, chatModel: ChatModel, close: (() -> Unit)?, chatDe
|
|||
val chatRh = chat.remoteHostId
|
||||
val ct = chatModel.controller.apiDeleteContact(chatRh, chatInfo.apiId, chatDeleteMode)
|
||||
if (ct != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
when (chatDeleteMode) {
|
||||
is ChatDeleteMode.Full ->
|
||||
removeChat(chatRh, chatInfo.id)
|
||||
chatModel.chatsContext.removeChat(chatRh, chatInfo.id)
|
||||
is ChatDeleteMode.Entity ->
|
||||
updateContact(chatRh, ct)
|
||||
chatModel.chatsContext.updateContact(chatRh, ct)
|
||||
is ChatDeleteMode.Messages ->
|
||||
clearChat(chatRh, ChatInfo.Direct(ct))
|
||||
chatModel.chatsContext.clearChat(chatRh, ChatInfo.Direct(ct))
|
||||
}
|
||||
}
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
|
@ -1270,11 +1268,11 @@ suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, c
|
|||
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
||||
|
||||
if (controller.apiSetChatUIThemes(chat.remoteHostId, chat.id, changedThemes)) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (chat.chatInfo is ChatInfo.Direct) {
|
||||
updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(contact = chat.chatInfo.contact.copy(uiThemes = changedThemes)))
|
||||
chatModel.chatsContext.updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(contact = chat.chatInfo.contact.copy(uiThemes = changedThemes)))
|
||||
} else if (chat.chatInfo is ChatInfo.Group) {
|
||||
updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(groupInfo = chat.chatInfo.groupInfo.copy(uiThemes = changedThemes)))
|
||||
chatModel.chatsContext.updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(groupInfo = chat.chatInfo.groupInfo.copy(uiThemes = changedThemes)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1283,8 +1281,8 @@ suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, c
|
|||
private fun setContactAlias(chat: Chat, localAlias: String, chatModel: ChatModel) = withBGApi {
|
||||
val chatRh = chat.remoteHostId
|
||||
chatModel.controller.apiSetContactAlias(chatRh, chat.chatInfo.apiId, localAlias)?.let {
|
||||
withChats {
|
||||
updateContact(chatRh, it)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(chatRh, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1386,10 +1384,10 @@ private suspend fun afterSetChatTTL(rhId: Long?, chatInfo: ChatInfo, progressInd
|
|||
val (chat, navInfo) = controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId, null, pagination) ?: return
|
||||
if (chat.chatItems.isEmpty()) {
|
||||
// replacing old chat with the same old chat but without items. Less intrusive way of clearing a preview
|
||||
withChats {
|
||||
val oldChat = getChat(chat.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
val oldChat = chatModel.chatsContext.getChat(chat.id)
|
||||
if (oldChat != null) {
|
||||
replaceChat(oldChat.remoteHostId, oldChat.id, oldChat.copy(chatItems = emptyList()))
|
||||
chatModel.chatsContext.replaceChat(oldChat.remoteHostId, oldChat.id, oldChat.copy(chatItems = emptyList()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package chat.simplex.common.views.chat
|
|||
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -48,29 +47,30 @@ suspend fun processLoadedChat(
|
|||
openAroundItemId: Long?,
|
||||
visibleItemIndexesNonReversed: () -> IntRange = { 0 .. 0 }
|
||||
) {
|
||||
val chatState = chatModel.chatStateForContent(contentTag)
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val chatState = chatsCtx.chatState
|
||||
val (splits, unreadAfterItemId, totalAfter, unreadTotal, unreadAfter, unreadAfterNewestLoaded) = chatState
|
||||
val oldItems = chatModel.chatItemsForContent(contentTag).value
|
||||
val oldItems = chatsCtx.chatItems.value
|
||||
val newItems = SnapshotStateList<ChatItem>()
|
||||
when (pagination) {
|
||||
is ChatPagination.Initial -> {
|
||||
val newSplits = if (chat.chatItems.isNotEmpty() && navInfo.afterTotal > 0) listOf(chat.chatItems.last().id) else emptyList()
|
||||
if (contentTag == null) {
|
||||
// update main chats, not content tagged
|
||||
withChats {
|
||||
val oldChat = getChat(chat.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
val oldChat = chatModel.chatsContext.getChat(chat.id)
|
||||
if (oldChat == null) {
|
||||
addChat(chat)
|
||||
chatModel.chatsContext.addChat(chat)
|
||||
} else {
|
||||
updateChatInfo(chat.remoteHostId, chat.chatInfo)
|
||||
chatModel.chatsContext.updateChatInfo(chat.remoteHostId, chat.chatInfo)
|
||||
// unreadChat is currently not actual in getChat query (always false)
|
||||
updateChatStats(chat.remoteHostId, chat.id, chat.chatStats.copy(unreadChat = oldChat.chatStats.unreadChat))
|
||||
chatModel.chatsContext.updateChatStats(chat.remoteHostId, chat.id, chat.chatStats.copy(unreadChat = oldChat.chatStats.unreadChat))
|
||||
}
|
||||
}
|
||||
}
|
||||
withChats(contentTag) {
|
||||
chatItemStatuses.clear()
|
||||
chatItems.replaceAll(chat.chatItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsCtx.chatItemStatuses.clear()
|
||||
chatsCtx.chatItems.replaceAll(chat.chatItems)
|
||||
chatModel.chatId.value = chat.id
|
||||
splits.value = newSplits
|
||||
if (chat.chatItems.isNotEmpty()) {
|
||||
|
@ -93,8 +93,8 @@ suspend fun processLoadedChat(
|
|||
)
|
||||
val insertAt = (indexInCurrentItems - (wasSize - newItems.size) + trimmedIds.size).coerceAtLeast(0)
|
||||
newItems.addAll(insertAt, chat.chatItems)
|
||||
withChats(contentTag) {
|
||||
chatItems.replaceAll(newItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsCtx.chatItems.replaceAll(newItems)
|
||||
splits.value = newSplits
|
||||
chatState.moveUnreadAfterItem(oldUnreadSplitIndex, newUnreadSplitIndex, oldItems)
|
||||
}
|
||||
|
@ -112,8 +112,8 @@ suspend fun processLoadedChat(
|
|||
val indexToAdd = min(indexInCurrentItems + 1, newItems.size)
|
||||
val indexToAddIsLast = indexToAdd == newItems.size
|
||||
newItems.addAll(indexToAdd, chat.chatItems)
|
||||
withChats(contentTag) {
|
||||
chatItems.replaceAll(newItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsCtx.chatItems.replaceAll(newItems)
|
||||
splits.value = newSplits
|
||||
chatState.moveUnreadAfterItem(splits.value.firstOrNull() ?: newItems.last().id, newItems)
|
||||
// loading clear bottom area, updating number of unread items after the newest loaded item
|
||||
|
@ -134,8 +134,8 @@ suspend fun processLoadedChat(
|
|||
newItems.addAll(itemIndex, chat.chatItems)
|
||||
newSplits.add(splitIndex, chat.chatItems.last().id)
|
||||
|
||||
withChats(contentTag) {
|
||||
chatItems.replaceAll(newItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsCtx.chatItems.replaceAll(newItems)
|
||||
splits.value = newSplits
|
||||
unreadAfterItemId.value = chat.chatItems.last().id
|
||||
totalAfter.value = navInfo.afterTotal
|
||||
|
@ -157,8 +157,8 @@ suspend fun processLoadedChat(
|
|||
val newSplits = removeDuplicatesAndUnusedSplits(newItems, chat, chatState.splits.value)
|
||||
removeDuplicates(newItems, chat)
|
||||
newItems.addAll(chat.chatItems)
|
||||
withChats(contentTag) {
|
||||
chatItems.replaceAll(newItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatsCtx.chatItems.replaceAll(newItems)
|
||||
chatState.splits.value = newSplits
|
||||
unreadAfterNewestLoaded.value = 0
|
||||
}
|
||||
|
|
|
@ -32,8 +32,6 @@ import chat.simplex.common.model.CIDirection.GroupRcv
|
|||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.activeCall
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.call.*
|
||||
import chat.simplex.common.views.chat.group.*
|
||||
|
@ -114,9 +112,10 @@ 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 {
|
||||
chatModel.chatsForContent(contentTag).value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatStats?.unreadCount ?: 0
|
||||
chatsCtx.chats.value.firstOrNull { chat -> chat.chatInfo.id == staleChatId.value }?.chatStats?.unreadCount ?: 0
|
||||
}
|
||||
}
|
||||
val clipboard = LocalClipboardManager.current
|
||||
|
@ -389,23 +388,25 @@ fun ChatView(
|
|||
if (deleted != null) {
|
||||
deletedChatItem = deleted.deletedChatItem.chatItem
|
||||
toChatItem = deleted.toChatItem?.chatItem
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
chatModel.chatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, deletedChatItem)
|
||||
chatModel.chatsContext.removeChatItem(chatRh, chatInfo, deletedChatItem)
|
||||
}
|
||||
val deletedItem = deleted.deletedChatItem.chatItem
|
||||
if (deletedItem.isActiveReport) {
|
||||
decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (deletedChatItem.isReport) {
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, deletedChatItem)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,8 +464,8 @@ fun ChatView(
|
|||
if (r != null) {
|
||||
val contactStats = r.first
|
||||
if (contactStats != null)
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, contactStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(chatRh, contact, contactStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,8 +476,8 @@ fun ChatView(
|
|||
if (r != null) {
|
||||
val memStats = r.second
|
||||
if (memStats != null) {
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(chatRh, groupInfo, r.first, memStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(chatRh, groupInfo, r.first, memStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,8 +487,8 @@ fun ChatView(
|
|||
withBGApi {
|
||||
val cStats = chatModel.controller.apiSyncContactRatchet(chatRh, contact.contactId, force = false)
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, cStats)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnectionStats(chatRh, contact, cStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -496,8 +497,8 @@ fun ChatView(
|
|||
withBGApi {
|
||||
val r = chatModel.controller.apiSyncGroupMemberRatchet(chatRh, groupInfo.apiId, member.groupMemberId, force = false)
|
||||
if (r != null) {
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(chatRh, groupInfo, r.first, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(chatRh, groupInfo, r.first, r.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -519,12 +520,14 @@ fun ChatView(
|
|||
reaction = reaction
|
||||
)
|
||||
if (updatedCI != null) {
|
||||
withChats {
|
||||
updateChatItem(cInfo, updatedCI)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.isReport) {
|
||||
updateChatItem(cInfo, updatedCI)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
if (cItem.isReport) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -544,7 +547,7 @@ fun ChatView(
|
|||
groupMembersJob.cancel()
|
||||
groupMembersJob = scope.launch(Dispatchers.Default) {
|
||||
var initialCiInfo = loadChatItemInfo() ?: return@launch
|
||||
if (!ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) {
|
||||
if (!ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
ModalManager.end.showModalCloseable(endButtons = {
|
||||
|
@ -578,11 +581,8 @@ fun ChatView(
|
|||
openGroupLink = { groupInfo -> openGroupLink(view = view, groupInfo = groupInfo, rhId = chatRh, close = { ModalManager.end.closeModals() }) },
|
||||
markItemsRead = { itemsIds ->
|
||||
withBGApi {
|
||||
withChats {
|
||||
// It's important to call it on Main thread. Otherwise, composable crash occurs from time-to-time without useful stacktrace
|
||||
withContext(Dispatchers.Main) {
|
||||
markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
chatModel.controller.apiChatItemsRead(
|
||||
chatRh,
|
||||
|
@ -591,18 +591,17 @@ fun ChatView(
|
|||
itemsIds
|
||||
)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.markChatItemsRead(chatRh, chatInfo.id, itemsIds)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
markChatRead = {
|
||||
withBGApi {
|
||||
withChats {
|
||||
// It's important to call it on Main thread. Otherwise, composable crash occurs from time-to-time without useful stacktrace
|
||||
withContext(Dispatchers.Main) {
|
||||
markChatItemsRead(chatRh, chatInfo.id)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.markChatItemsRead(chatRh, chatInfo.id)
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
chatModel.controller.apiChatRead(
|
||||
chatRh,
|
||||
|
@ -610,8 +609,10 @@ fun ChatView(
|
|||
chatInfo.apiId
|
||||
)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
markChatItemsRead(chatRh, chatInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.markChatItemsRead(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -636,8 +637,8 @@ fun ChatView(
|
|||
LaunchedEffect(chatInfo.id) {
|
||||
onComposed(chatInfo.id)
|
||||
ModalManager.end.closeModals()
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -649,8 +650,8 @@ fun ChatView(
|
|||
LaunchedEffect(chatInfo.id) {
|
||||
onComposed(chatInfo.id)
|
||||
ModalManager.end.closeModals()
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1206,12 +1207,13 @@ fun BoxScope.ChatItemsList(
|
|||
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(chatModel.chatItemsForContent(contentTag).value.asReversed(), unreadCount, revealedItems.value, chatModel.chatStateForContent(contentTag))
|
||||
MergedItems.create(chatsCtx.chatItems.value.asReversed(), unreadCount, revealedItems.value, chatsCtx.chatState)
|
||||
}
|
||||
}
|
||||
val reversedChatItems = remember { derivedStateOf { chatModel.chatItemsForContent(contentTag).value.asReversed() } }
|
||||
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() })
|
||||
|
@ -1297,11 +1299,11 @@ fun BoxScope.ChatItemsList(
|
|||
|
||||
DisposableEffectOnGone(
|
||||
always = {
|
||||
chatModel.setChatItemsChangeListenerForContent(recalculateChatStatePositions(chatModel.chatStateForContent(contentTag)), contentTag)
|
||||
chatsCtx.chatItemsChangesListener = recalculateChatStatePositions(chatsCtx.chatState)
|
||||
},
|
||||
whenGone = {
|
||||
VideoPlayerHolder.releaseAll()
|
||||
chatModel.setChatItemsChangeListenerForContent(recalculateChatStatePositions(chatModel.chatStateForContent(contentTag)), contentTag)
|
||||
chatsCtx.chatItemsChangesListener = recalculateChatStatePositions(chatsCtx.chatState)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1648,8 +1650,9 @@ private suspend fun loadLastItems(chatId: State<ChatId>, contentTag: MsgContentT
|
|||
}
|
||||
|
||||
private fun lastItemsLoaded(contentTag: MsgContentTag?): Boolean {
|
||||
val chatState = chatModel.chatStateForContent(contentTag)
|
||||
return chatState.splits.value.isEmpty() || chatState.splits.value.firstOrNull() != chatModel.chatItemsForContent(contentTag).value.lastOrNull()?.id
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val chatState = chatsCtx.chatState
|
||||
return chatState.splits.value.isEmpty() || chatState.splits.value.firstOrNull() != chatsCtx.chatItems.value.lastOrNull()?.id
|
||||
}
|
||||
|
||||
// TODO: in extra rare case when after loading last items only 1 item is loaded, the view will jump like when receiving new message
|
||||
|
@ -1740,7 +1743,8 @@ fun BoxScope.FloatingButtons(
|
|||
fun scrollToTopUnread() {
|
||||
scope.launch {
|
||||
tryBlockAndSetLoadingMore(loadingMoreItems) {
|
||||
if (chatModel.chatStateForContent(contentTag).splits.value.isNotEmpty()) {
|
||||
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
|
||||
loadMessages(chatInfo.value.id, pagination) {
|
||||
|
@ -2129,7 +2133,7 @@ private fun SaveReportsStateOnDispose(listState: State<LazyListState>) {
|
|||
val contentTag = LocalContentTag.current
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
reportsListState = if (contentTag == MsgContentTag.Report && ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) listState.value else null
|
||||
reportsListState = if (contentTag == MsgContentTag.Report && ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) listState.value else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2240,8 +2244,10 @@ fun reportsCount(staleChatId: String?): Int {
|
|||
}
|
||||
}
|
||||
|
||||
private fun reversedChatItemsStatic(contentTag: MsgContentTag?): List<ChatItem> =
|
||||
chatModel.chatItemsForContent(contentTag).value.asReversed()
|
||||
private fun reversedChatItemsStatic(contentTag: MsgContentTag?): List<ChatItem> {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
return 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
|
||||
|
@ -2326,11 +2332,13 @@ private fun findQuotedItemFromItem(
|
|||
scope.launch(Dispatchers.Default) {
|
||||
val item = apiLoadSingleMessage(rhId.value, chatInfo.value.chatType, chatInfo.value.apiId, itemId, contentTag)
|
||||
if (item != null) {
|
||||
withChats {
|
||||
updateChatItem(chatInfo.value, item)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatItem(chatInfo.value, item)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateChatItem(chatInfo.value, item)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.updateChatItem(chatInfo.value, item)
|
||||
}
|
||||
}
|
||||
if (item.quotedItem?.itemId != null) {
|
||||
scrollToItem(item.quotedItem.itemId)
|
||||
|
@ -2510,28 +2518,30 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
|||
)
|
||||
}
|
||||
if (deleted != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
for (di in deleted) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
chatModel.chatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
chatModel.chatsContext.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
val deletedItem = di.deletedChatItem.chatItem
|
||||
if (deletedItem.isActiveReport) {
|
||||
decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2552,28 +2562,30 @@ private fun archiveReports(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
|||
mode = if (forAll) CIDeleteMode.cidmBroadcast else CIDeleteMode.cidmInternalMark
|
||||
)
|
||||
if (deleted != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
for (di in deleted) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
chatModel.chatsContext.upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
chatModel.chatsContext.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
val deletedItem = di.deletedChatItem.chatItem
|
||||
if (deletedItem.isActiveReport) {
|
||||
decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
chatModel.chatsContext.decreaseGroupReportsCounter(chatRh, chatInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
for (di in deleted) {
|
||||
if (di.deletedChatItem.chatItem.isReport) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2607,9 +2619,9 @@ private fun markUnreadChatAsRead(chatId: String) {
|
|||
false
|
||||
)
|
||||
if (success) {
|
||||
withChats {
|
||||
replaceChat(chatRh, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
markChatTagRead(chat)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.replaceChat(chatRh, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
chatModel.chatsContext.markChatTagRead(chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2791,7 +2803,7 @@ private fun ViewConfiguration.bigTouchSlop(slop: Float = 50f) = object: ViewConf
|
|||
private fun forwardContent(chatItemsIds: List<Long>, chatInfo: ChatInfo) {
|
||||
chatModel.chatId.value = null
|
||||
chatModel.sharedContent.value = SharedContent.Forward(
|
||||
chatModel.chatItemsForContent(null).value.filter { chatItemsIds.contains(it.id) },
|
||||
chatModel.chatsContext.chatItems.value.filter { chatItemsIds.contains(it.id) },
|
||||
chatInfo
|
||||
)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import androidx.compose.ui.util.*
|
|||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.filesToDelete
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.*
|
||||
|
@ -473,8 +472,8 @@ fun ComposeView(
|
|||
)
|
||||
if (!chatItems.isNullOrEmpty()) {
|
||||
chatItems.forEach { aChatItem ->
|
||||
withChats {
|
||||
addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
|
||||
}
|
||||
}
|
||||
return chatItems.first().chatItem
|
||||
|
@ -505,9 +504,9 @@ fun ComposeView(
|
|||
ttl = ttl
|
||||
)
|
||||
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatItems?.forEach { chatItem ->
|
||||
addChatItem(rhId, chat.chatInfo, chatItem)
|
||||
chatModel.chatsContext.addChatItem(rhId, chat.chatInfo, chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,9 +566,9 @@ fun ComposeView(
|
|||
suspend fun sendReport(reportReason: ReportReason, chatItemId: Long): List<ChatItem>? {
|
||||
val cItems = chatModel.controller.apiReportMessage(chat.remoteHostId, chat.chatInfo.apiId, chatItemId, reportReason, msgText)
|
||||
if (cItems != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
cItems.forEach { chatItem ->
|
||||
addChatItem(chat.remoteHostId, chat.chatInfo, chatItem.chatItem)
|
||||
chatModel.chatsContext.addChatItem(chat.remoteHostId, chat.chatInfo, chatItem.chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -581,8 +580,8 @@ fun ComposeView(
|
|||
val mc = checkLinkPreview()
|
||||
val contact = chatModel.controller.apiSendMemberContactInvitation(chat.remoteHostId, chat.chatInfo.apiId, mc)
|
||||
if (contact != null) {
|
||||
withChats {
|
||||
updateContact(chat.remoteHostId, contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(chat.remoteHostId, contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -599,8 +598,10 @@ fun ComposeView(
|
|||
updatedMessage = UpdatedMessage(updateMsgContent(oldMsgContent), cs.memberMentions),
|
||||
live = live
|
||||
)
|
||||
if (updatedItem != null) withChats {
|
||||
upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem)
|
||||
if (updatedItem != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertChatItem(chat.remoteHostId, cInfo, updatedItem.chatItem)
|
||||
}
|
||||
}
|
||||
return updatedItem?.chatItem
|
||||
}
|
||||
|
@ -890,7 +891,7 @@ fun ComposeView(
|
|||
|
||||
fun editPrevMessage() {
|
||||
if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return
|
||||
val lastEditable = chatModel.chatItemsForContent(null).value.findLast { it.meta.editable }
|
||||
val lastEditable = chatModel.chatsContext.chatItems.value.findLast { it.meta.editable }
|
||||
if (lastEditable != null) {
|
||||
composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews)
|
||||
}
|
||||
|
|
|
@ -12,16 +12,16 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.PreferenceToggle
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun ContactPreferencesView(
|
||||
|
@ -41,8 +41,8 @@ fun ContactPreferencesView(
|
|||
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
|
||||
val toContact = m.controller.apiSetContactPrefs(rhId, ct.contactId, prefs)
|
||||
if (toContact != null) {
|
||||
withChats {
|
||||
updateContact(rhId, toContact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, toContact)
|
||||
currentFeaturesAllowed = featuresAllowed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ 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
|
||||
|
@ -121,7 +122,8 @@ fun SelectedItemsButtonsToolbar(
|
|||
}
|
||||
Divider(Modifier.align(Alignment.TopStart))
|
||||
}
|
||||
val chatItems = remember { derivedStateOf { chatModel.chatItemsForContent(contentTag).value } }
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.ChatInfoToolbarTitle
|
||||
import chat.simplex.common.views.helpers.*
|
||||
|
@ -35,6 +33,7 @@ import chat.simplex.common.model.GroupInfo
|
|||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolean = false, chatModel: ChatModel, close: () -> Unit) {
|
||||
|
@ -62,11 +61,13 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
|
|||
for (contactId in selectedContacts) {
|
||||
val member = chatModel.controller.apiAddMember(rhId, groupInfo.groupId, contactId, selectedRole.value)
|
||||
if (member != null) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
|
|
|
@ -32,8 +32,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
|
@ -182,8 +180,8 @@ fun deleteGroupDialog(chat: Chat, groupInfo: GroupInfo, chatModel: ChatModel, cl
|
|||
withBGApi {
|
||||
val r = chatModel.controller.apiDeleteChat(chat.remoteHostId, chatInfo.chatType, chatInfo.apiId)
|
||||
if (r) {
|
||||
withChats {
|
||||
removeChat(chat.remoteHostId, chatInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(chat.remoteHostId, chatInfo.id)
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
ModalManager.end.closeModals()
|
||||
|
@ -957,8 +955,8 @@ private fun SearchRowView(
|
|||
private fun setGroupAlias(chat: Chat, localAlias: String, chatModel: ChatModel) = withBGApi {
|
||||
val chatRh = chat.remoteHostId
|
||||
chatModel.controller.apiSetGroupAlias(chatRh, chat.chatInfo.apiId, localAlias)?.let {
|
||||
withChats {
|
||||
updateGroup(chatRh, it)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(chatRh, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -967,14 +965,16 @@ fun removeMembers(rhId: Long?, groupInfo: GroupInfo, memberIds: List<Long>, onSu
|
|||
withBGApi {
|
||||
val updatedMembers = chatModel.controller.apiRemoveMembers(rhId, groupInfo.groupId, memberIds)
|
||||
if (updatedMembers != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
|
|
@ -27,8 +27,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
|
@ -40,6 +38,7 @@ import chat.simplex.common.views.chatlist.openLoadedChat
|
|||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun GroupMemberInfoView(
|
||||
|
@ -63,11 +62,13 @@ fun GroupMemberInfoView(
|
|||
val r = chatModel.controller.apiSyncGroupMemberRatchet(rhId, groupInfo.apiId, member.groupMemberId, force = false)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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)
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
|
@ -100,8 +101,8 @@ fun GroupMemberInfoView(
|
|||
val memberContact = chatModel.controller.apiCreateMemberContact(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (memberContact != null) {
|
||||
val memberChat = Chat(remoteHostId = rhId, ChatInfo.Direct(memberContact), chatItems = arrayListOf())
|
||||
withChats {
|
||||
addChat(memberChat)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.addChat(memberChat)
|
||||
}
|
||||
openLoadedChat(memberChat)
|
||||
closeAll()
|
||||
|
@ -149,11 +150,13 @@ fun GroupMemberInfoView(
|
|||
val r = chatModel.controller.apiSwitchGroupMember(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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)
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
|
@ -166,11 +169,13 @@ fun GroupMemberInfoView(
|
|||
val r = chatModel.controller.apiAbortSwitchGroupMember(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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)
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
|
@ -186,11 +191,13 @@ fun GroupMemberInfoView(
|
|||
val r = chatModel.controller.apiSyncGroupMemberRatchet(rhId, groupInfo.apiId, member.groupMemberId, force = true)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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)
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
|
@ -212,11 +219,13 @@ fun GroupMemberInfoView(
|
|||
connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
|
||||
)
|
||||
)
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, copy)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, copy)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
@ -247,14 +256,16 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
|||
withBGApi {
|
||||
val removedMembers = chatModel.controller.apiRemoveMembers(rhId, member.groupId, listOf(member.groupMemberId))
|
||||
if (removedMembers != null) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
removedMembers.forEach { removedMember ->
|
||||
upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
removedMembers.forEach { removedMember ->
|
||||
upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
removedMembers.forEach { removedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -697,14 +708,16 @@ fun updateMembersRole(newRole: GroupMemberRole, rhId: Long?, groupInfo: GroupInf
|
|||
withBGApi {
|
||||
kotlin.runCatching {
|
||||
val members = chatModel.controller.apiMembersRole(rhId, groupInfo.groupId, memberIds, newRole)
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
members.forEach { member ->
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
members.forEach { member ->
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
members.forEach { member ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
@ -798,11 +811,13 @@ fun updateMemberSettings(rhId: Long?, gInfo: GroupInfo, member: GroupMember, mem
|
|||
withBGApi {
|
||||
val success = ChatController.apiSetMemberSettings(rhId, gInfo.groupId, member.groupMemberId, memberSettings)
|
||||
if (success) {
|
||||
withChats {
|
||||
upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -857,14 +872,16 @@ fun unblockForAllAlert(rhId: Long?, gInfo: GroupInfo, memberIds: List<Long>, onS
|
|||
fun blockMemberForAll(rhId: Long?, gInfo: GroupInfo, memberIds: List<Long>, blocked: Boolean, onSuccess: () -> Unit = {}) {
|
||||
withBGApi {
|
||||
val updatedMembers = ChatController.apiBlockMembersForAll(rhId, gInfo.groupId, memberIds, blocked)
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
chatModel.chatsContext.upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
updatedMembers.forEach { updatedMember ->
|
||||
chatModel.secondaryChatsContext.upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
|
|
|
@ -15,9 +15,10 @@ import chat.simplex.common.ui.theme.*
|
|||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.PreferenceToggleWithIcon
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
private val featureRoles: List<Pair<GroupMemberRole?, String>> = listOf(
|
||||
null to generalGetString(MR.strings.feature_roles_all_members),
|
||||
|
@ -42,12 +43,12 @@ fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () ->
|
|||
val gp = gInfo.groupProfile.copy(groupPreferences = preferences.toGroupPreferences())
|
||||
val g = m.controller.apiUpdateGroup(rhId, gInfo.groupId, gp)
|
||||
if (g != null) {
|
||||
withChats {
|
||||
updateGroup(rhId, g)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, g)
|
||||
currentPreferences = preferences
|
||||
}
|
||||
withChats {
|
||||
updateGroup(rhId, g)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, g)
|
||||
}
|
||||
}
|
||||
afterSave()
|
||||
|
|
|
@ -17,8 +17,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.*
|
||||
|
@ -27,8 +25,7 @@ import chat.simplex.common.views.onboarding.ReadableText
|
|||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
|
@ -40,8 +37,8 @@ fun GroupProfileView(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl
|
|||
withBGApi {
|
||||
val gInfo = chatModel.controller.apiUpdateGroup(rhId, groupInfo.groupId, p)
|
||||
if (gInfo != null) {
|
||||
withChats {
|
||||
updateGroup(rhId, gInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, gInfo)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ private fun ItemsReload(contentTag: MsgContentTag?) {
|
|||
|
||||
suspend fun showGroupReportsView(staleChatId: State<String?>, scrollToItemId: MutableState<Long?>, chatInfo: ChatInfo) {
|
||||
openChat(chatModel.remoteHostId(), chatInfo, MsgContentTag.Report)
|
||||
ModalManager.end.showCustomModal(true, id = ModalViewId.GROUP_REPORTS) { close ->
|
||||
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) {
|
||||
|
|
|
@ -26,13 +26,10 @@ import chat.simplex.common.ui.theme.DEFAULT_PADDING
|
|||
import chat.simplex.common.views.chat.item.MarkdownText
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.model.GroupInfo
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatJsonLength
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
private const val maxByteCount = 1200
|
||||
|
||||
|
@ -51,8 +48,8 @@ fun GroupWelcomeView(m: ChatModel, rhId: Long?, groupInfo: GroupInfo, close: ()
|
|||
val res = m.controller.apiUpdateGroup(rhId, gInfo.groupId, groupProfileUpdated)
|
||||
if (res != null) {
|
||||
gInfo = res
|
||||
withChats {
|
||||
updateGroup(rhId, res)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, res)
|
||||
}
|
||||
welcomeText.value = welcome ?: ""
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ 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
|
||||
|
||||
|
@ -76,7 +77,8 @@ private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<Feature
|
|||
val m = ChatModel
|
||||
val fs: ArrayList<FeatureInfo> = arrayListOf()
|
||||
val icons: MutableSet<PainterBox> = mutableSetOf()
|
||||
val reversedChatItems = m.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
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) {
|
||||
while (i < reversedChatItems.size) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import androidx.compose.material.*
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
|
@ -632,7 +631,8 @@ fun ChatItemView(
|
|||
}
|
||||
|
||||
@Composable fun EventItemView() {
|
||||
val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
val chatsCtx = if (LocalContentTag.current == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
CIEventView(eventItemViewText(reversedChatItems))
|
||||
}
|
||||
|
||||
|
@ -839,13 +839,14 @@ fun DeleteItemAction(
|
|||
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),
|
||||
onClick = {
|
||||
showMenu.value = false
|
||||
if (!revealed.value) {
|
||||
val reversedChatItems = chatModel.chatItemsForContent(contentTag).value.asReversed()
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
val currIndex = chatModel.getChatItemIndexOrNull(cItem, reversedChatItems)
|
||||
val ciCategory = cItem.mergeCategory
|
||||
if (currIndex != null && ciCategory != null) {
|
||||
|
@ -1314,7 +1315,7 @@ fun shapeStyle(chatItem: ChatItem? = null, tailEnabled: Boolean, tailVisible: Bo
|
|||
}
|
||||
|
||||
private fun closeReportsIfNeeded() {
|
||||
if (appPlatform.isAndroid && ModalManager.end.isLastModalOpen(ModalViewId.GROUP_REPORTS)) {
|
||||
if (appPlatform.isAndroid && ModalManager.end.isLastModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ fun MarkedDeletedItemView(ci: ChatItem, chatInfo: ChatInfo, timedMessagesTTL: In
|
|||
|
||||
@Composable
|
||||
private fun MergedMarkedDeletedText(chatItem: ChatItem, chatInfo: ChatInfo, revealed: State<Boolean>) {
|
||||
val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
val chatsCtx = if (LocalContentTag.current == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
val reversedChatItems = chatsCtx.chatItems.value.asReversed()
|
||||
var i = getChatItemIndexOrNull(chatItem, reversedChatItems)
|
||||
val ciCategory = chatItem.mergeCategory
|
||||
val text = if (!revealed.value && ciCategory != null && i != null) {
|
||||
|
|
|
@ -20,8 +20,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
|
@ -233,17 +231,19 @@ suspend fun openChat(
|
|||
)
|
||||
|
||||
suspend fun openLoadedChat(chat: Chat, contentTag: MsgContentTag? = null) {
|
||||
withChats(contentTag) {
|
||||
chatItemStatuses.clear()
|
||||
chatItems.replaceAll(chat.chatItems)
|
||||
withContext(Dispatchers.Main) {
|
||||
val chatsCtx = if (contentTag == null) chatModel.chatsContext else chatModel.secondaryChatsContext
|
||||
chatsCtx.chatItemStatuses.clear()
|
||||
chatsCtx.chatItems.replaceAll(chat.chatItems)
|
||||
chatModel.chatId.value = chat.chatInfo.id
|
||||
chatModel.chatStateForContent(contentTag).clear()
|
||||
chatsCtx.chatState.clear()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiFindMessages(ch: Chat, search: String, contentTag: MsgContentTag?) {
|
||||
withChats(contentTag) {
|
||||
chatItems.clearAndNotify()
|
||||
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)
|
||||
}
|
||||
|
@ -604,11 +604,13 @@ fun markChatRead(c: Chat) {
|
|||
var chat = c
|
||||
withApi {
|
||||
if (chat.chatStats.unreadCount > 0) {
|
||||
withChats {
|
||||
markChatItemsRead(chat.remoteHostId, chat.chatInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.markChatItemsRead(chat.remoteHostId, chat.chatInfo.id)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
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.controller.apiChatRead(
|
||||
chat.remoteHostId,
|
||||
|
@ -625,9 +627,9 @@ fun markChatRead(c: Chat) {
|
|||
false
|
||||
)
|
||||
if (success) {
|
||||
withChats {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
markChatTagRead(chat)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
chatModel.chatsContext.markChatTagRead(chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -647,9 +649,9 @@ fun markChatUnread(chat: Chat, chatModel: ChatModel) {
|
|||
true
|
||||
)
|
||||
if (success) {
|
||||
withChats {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true)))
|
||||
updateChatTagReadNoContentTag(chat, wasUnread)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true)))
|
||||
chatModel.chatsContext.updateChatTagReadNoContentTag(chat, wasUnread)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -690,8 +692,8 @@ fun acceptContactRequest(rhId: Long?, incognito: Boolean, apiId: Long, contactRe
|
|||
val contact = chatModel.controller.apiAcceptContactRequest(rhId, incognito, apiId)
|
||||
if (contact != null && isCurrentUser && contactRequest != null) {
|
||||
val chat = Chat(remoteHostId = rhId, ChatInfo.Direct(contact), listOf())
|
||||
withChats {
|
||||
replaceChat(rhId, contactRequest.id, chat)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.replaceChat(rhId, contactRequest.id, chat)
|
||||
}
|
||||
chatModel.setContactNetworkStatus(contact, NetworkStatus.Connected())
|
||||
close?.invoke(chat)
|
||||
|
@ -702,8 +704,8 @@ fun acceptContactRequest(rhId: Long?, incognito: Boolean, apiId: Long, contactRe
|
|||
fun rejectContactRequest(rhId: Long?, contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) {
|
||||
withBGApi {
|
||||
chatModel.controller.apiRejectContactRequest(rhId, contactRequest.apiId)
|
||||
withChats {
|
||||
removeChat(rhId, contactRequest.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, contactRequest.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -720,8 +722,8 @@ fun deleteContactConnectionAlert(rhId: Long?, connection: PendingContactConnecti
|
|||
withBGApi {
|
||||
AlertManager.shared.hideAlert()
|
||||
if (chatModel.controller.apiDeleteChat(rhId, ChatType.ContactConnection, connection.apiId)) {
|
||||
withChats {
|
||||
removeChat(rhId, connection.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, connection.id)
|
||||
}
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -741,8 +743,8 @@ fun pendingContactAlertDialog(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatMo
|
|||
withBGApi {
|
||||
val r = chatModel.controller.apiDeleteChat(rhId, chatInfo.chatType, chatInfo.apiId)
|
||||
if (r) {
|
||||
withChats {
|
||||
removeChat(rhId, chatInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, chatInfo.id)
|
||||
}
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
|
@ -805,8 +807,8 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress(
|
|||
suspend fun connectContactViaAddress(chatModel: ChatModel, rhId: Long?, contactId: Long, incognito: Boolean): Boolean {
|
||||
val contact = chatModel.controller.apiConnectContactViaAddress(rhId, incognito, contactId)
|
||||
if (contact != null) {
|
||||
withChats {
|
||||
updateContact(rhId, contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, contact)
|
||||
}
|
||||
AlertManager.privacySensitive.showAlertMsg(
|
||||
title = generalGetString(MR.strings.connection_request_sent),
|
||||
|
@ -848,8 +850,8 @@ fun deleteGroup(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) {
|
|||
withBGApi {
|
||||
val r = chatModel.controller.apiDeleteChat(rhId, ChatType.Group, groupInfo.apiId)
|
||||
if (r) {
|
||||
withChats {
|
||||
removeChat(rhId, groupInfo.id)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.removeChat(rhId, groupInfo.id)
|
||||
}
|
||||
if (chatModel.chatId.value == groupInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
|
@ -903,16 +905,16 @@ fun updateChatSettings(remoteHostId: Long?, chatInfo: ChatInfo, chatSettings: Ch
|
|||
val wasUnread = chat?.unreadTag ?: false
|
||||
val wasFavorite = chatInfo.chatSettings?.favorite ?: false
|
||||
chatModel.updateChatFavorite(favorite = chatSettings.favorite, wasFavorite)
|
||||
withChats {
|
||||
updateChatInfo(remoteHostId, newChatInfo)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatInfo(remoteHostId, newChatInfo)
|
||||
}
|
||||
if (chatSettings.enableNtfs == MsgFilter.None) {
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
}
|
||||
val updatedChat = chatModel.getChat(chatInfo.id)
|
||||
if (updatedChat != null) {
|
||||
withChats {
|
||||
updateChatTagReadNoContentTag(updatedChat, wasUnread)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatTagReadNoContentTag(updatedChat, wasUnread)
|
||||
}
|
||||
}
|
||||
val current = currentState?.value
|
||||
|
|
|
@ -27,7 +27,6 @@ import androidx.compose.ui.unit.*
|
|||
import chat.simplex.common.AppLock
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatController.setConditionsNotified
|
||||
import chat.simplex.common.model.ChatController.stopRemoteHostAndReloadHosts
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
|
@ -38,8 +37,6 @@ import chat.simplex.common.views.chat.topPaddingToContent
|
|||
import chat.simplex.common.views.newchat.*
|
||||
import chat.simplex.common.views.onboarding.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.common.views.usersettings.networkAndServers.ConditionsLinkButton
|
||||
import chat.simplex.common.views.usersettings.networkAndServers.UsageConditionsView
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
|
@ -139,7 +136,7 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow<Animate
|
|||
|
||||
if (appPlatform.isDesktop) {
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
if (chatModel.chatId.value != null && !ModalManager.end.isLastModalOpen(ModalViewId.GROUP_REPORTS)) {
|
||||
if (chatModel.chatId.value != null && !ModalManager.end.isLastModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
ModalManager.end.closeModalsExceptFirst()
|
||||
}
|
||||
AudioPlayer.stop()
|
||||
|
|
|
@ -32,8 +32,6 @@ import chat.simplex.common.model.ChatController.apiDeleteChatTag
|
|||
import chat.simplex.common.model.ChatController.apiSetChatTags
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.clearActiveChatFilterIfNeeded
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
|
@ -43,6 +41,7 @@ import chat.simplex.common.views.helpers.*
|
|||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun TagListView(rhId: Long?, chat: Chat? = null, close: () -> Unit, reorderMode: Boolean) {
|
||||
|
@ -417,15 +416,15 @@ private fun setTag(rhId: Long?, tagId: Long?, chat: Chat, close: () -> Unit) {
|
|||
when (val cInfo = chat.chatInfo) {
|
||||
is ChatInfo.Direct -> {
|
||||
val contact = cInfo.contact.copy(chatTags = result.second)
|
||||
withChats {
|
||||
updateContact(rhId, contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, contact)
|
||||
}
|
||||
}
|
||||
|
||||
is ChatInfo.Group -> {
|
||||
val group = cInfo.groupInfo.copy(chatTags = result.second)
|
||||
withChats {
|
||||
updateGroup(rhId, group)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, group)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -453,14 +452,14 @@ private fun deleteTag(rhId: Long?, tag: ChatTag, saving: MutableState<Boolean>)
|
|||
when (val cInfo = c.chatInfo) {
|
||||
is ChatInfo.Direct -> {
|
||||
val contact = cInfo.contact.copy(chatTags = cInfo.contact.chatTags.filter { it != tagId })
|
||||
withChats {
|
||||
updateContact(rhId, contact)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContact(rhId, contact)
|
||||
}
|
||||
}
|
||||
is ChatInfo.Group -> {
|
||||
val group = cInfo.groupInfo.copy(chatTags = cInfo.groupInfo.chatTags.filter { it != tagId })
|
||||
withChats {
|
||||
updateGroup(rhId, group)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId, group)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
|
|
|
@ -5,7 +5,6 @@ import androidx.compose.ui.graphics.Color
|
|||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
|
|
|
@ -21,8 +21,6 @@ import androidx.compose.ui.unit.dp
|
|||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
|
@ -36,6 +34,7 @@ import java.nio.file.StandardCopyOption
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun DatabaseView() {
|
||||
|
@ -538,15 +537,17 @@ fun deleteChatDatabaseFilesAndState() {
|
|||
// Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself
|
||||
chatModel.chatId.value = null
|
||||
withLongRunningApi {
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
chats.clear()
|
||||
popChatCollector.clear()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatsContext.chats.clear()
|
||||
chatModel.chatsContext.popChatCollector.clear()
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
chatItems.clearAndNotify()
|
||||
chats.clear()
|
||||
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.users.clear()
|
||||
|
@ -785,10 +786,10 @@ private fun afterSetCiTTL(
|
|||
appFilesCountAndSize.value = directoryFileCountAndSize(appFilesDir.absolutePath)
|
||||
withApi {
|
||||
try {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
// this is using current remote host on purpose - if it changes during update, it will load correct chats
|
||||
val chats = m.controller.apiGetChats(m.remoteHostId())
|
||||
updateChats(chats)
|
||||
chatModel.chatsContext.updateChats(chats)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "apiGetChats error: ${e.message}")
|
||||
|
|
|
@ -85,7 +85,7 @@ class ModalData(val keyboardCoversBar: Boolean = true) {
|
|||
}
|
||||
|
||||
enum class ModalViewId {
|
||||
GROUP_REPORTS
|
||||
SECONDARY_CHAT
|
||||
}
|
||||
|
||||
class ModalManager(private val placement: ModalPlacement? = null) {
|
||||
|
|
|
@ -18,7 +18,6 @@ import dev.icerock.moko.resources.compose.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.group.AddGroupMembersView
|
||||
import chat.simplex.common.views.chatlist.setGroupMembers
|
||||
|
@ -30,6 +29,7 @@ import chat.simplex.common.views.usersettings.*
|
|||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
|
@ -42,10 +42,10 @@ fun AddGroupView(chatModel: ChatModel, rh: RemoteHostInfo?, close: () -> Unit, c
|
|||
withBGApi {
|
||||
val groupInfo = chatModel.controller.apiNewGroup(rhId, incognito, groupProfile)
|
||||
if (groupInfo != null) {
|
||||
withChats {
|
||||
updateGroup(rhId = rhId, groupInfo)
|
||||
chatItems.clearAndNotify()
|
||||
chatItemStatuses.clear()
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId = rhId, groupInfo)
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
chatModel.chatsContext.chatItemStatuses.clear()
|
||||
chatModel.chatId.value = groupInfo.id
|
||||
}
|
||||
setGroupMembers(rhId, groupInfo, chatModel)
|
||||
|
|
|
@ -7,13 +7,11 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import java.net.URI
|
||||
|
||||
enum class ConnectionLinkType {
|
||||
INVITATION, CONTACT, GROUP
|
||||
|
@ -359,8 +357,8 @@ suspend fun connectViaUri(
|
|||
val pcc = chatModel.controller.apiConnect(rhId, incognito, uri)
|
||||
val connLinkType = if (connectionPlan != null) planToConnectionLinkType(connectionPlan) else ConnectionLinkType.INVITATION
|
||||
if (pcc != null) {
|
||||
withChats {
|
||||
updateContactConnection(rhId, pcc)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, pcc)
|
||||
}
|
||||
close?.invoke()
|
||||
AlertManager.privacySensitive.showAlertMsg(
|
||||
|
|
|
@ -5,7 +5,6 @@ import SectionDividerSpaced
|
|||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
|
@ -22,11 +21,11 @@ import chat.simplex.common.views.chat.LocalAliasEditor
|
|||
import chat.simplex.common.views.chatlist.deleteContactConnectionAlert
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.PendingContactConnection
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun ContactConnectionInfoView(
|
||||
|
@ -185,8 +184,8 @@ fun DeleteButton(onClick: () -> Unit) {
|
|||
|
||||
private fun setContactAlias(rhId: Long?, contactConnection: PendingContactConnection, localAlias: String, chatModel: ChatModel) = withBGApi {
|
||||
chatModel.controller.apiSetConnectionAlias(rhId, contactConnection.pccConnId, localAlias)?.let {
|
||||
withChats {
|
||||
updateContactConnection(rhId, it)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import androidx.compose.ui.unit.sp
|
|||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.topPaddingToContent
|
||||
|
@ -39,7 +38,6 @@ import chat.simplex.common.views.helpers.*
|
|||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import java.net.URI
|
||||
|
||||
enum class NewChatOption {
|
||||
INVITE, CONNECT
|
||||
|
@ -315,8 +313,8 @@ fun ActiveProfilePicker(
|
|||
if (contactConnection != null) {
|
||||
updatedConn = controller.apiChangeConnectionUser(rhId, contactConnection.pccConnId, user.userId)
|
||||
if (updatedConn != null) {
|
||||
withChats {
|
||||
updateContactConnection(rhId, updatedConn)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, updatedConn)
|
||||
updateShownConnection(updatedConn)
|
||||
}
|
||||
}
|
||||
|
@ -338,8 +336,8 @@ fun ActiveProfilePicker(
|
|||
}
|
||||
|
||||
if (updatedConn != null) {
|
||||
withChats {
|
||||
updateContactConnection(user.remoteHostId, updatedConn)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(user.remoteHostId, updatedConn)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,8 +366,8 @@ fun ActiveProfilePicker(
|
|||
appPreferences.incognito.set(true)
|
||||
val conn = controller.apiSetConnectionIncognito(rhId, contactConnection.pccConnId, true)
|
||||
if (conn != null) {
|
||||
withChats {
|
||||
updateContactConnection(rhId, conn)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, conn)
|
||||
updateShownConnection(conn)
|
||||
}
|
||||
close()
|
||||
|
@ -685,8 +683,8 @@ private fun createInvitation(
|
|||
withBGApi {
|
||||
val (r, alert) = controller.apiAddContact(rhId, incognito = controller.appPrefs.incognito.get())
|
||||
if (r != null) {
|
||||
withChats {
|
||||
updateContactConnection(rhId, r.second)
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, r.second)
|
||||
chatModel.showingInvitation.value = ShowingInvitation(connId = r.second.id, connReq = simplexChatLink(r.first), connChatUsed = false, conn = r.second)
|
||||
contactConnection.value = r.second
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun PreferencesView(m: ChatModel, user: User, close: () -> Unit,) {
|
||||
|
@ -34,8 +34,8 @@ fun PreferencesView(m: ChatModel, user: User, close: () -> Unit,) {
|
|||
if (updated != null) {
|
||||
val (updatedProfile, updatedContacts) = updated
|
||||
m.updateCurrentUser(user.remoteHostId, updatedProfile, preferences)
|
||||
withChats {
|
||||
updatedContacts.forEach { updateContact(user.remoteHostId, it) }
|
||||
withContext(Dispatchers.Main) {
|
||||
updatedContacts.forEach { chatModel.chatsContext.updateContact(user.remoteHostId, it) }
|
||||
}
|
||||
currentPreferences = preferences
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ import chat.simplex.common.views.isValidDisplayName
|
|||
import chat.simplex.common.views.localauth.SetAppPasscodeView
|
||||
import chat.simplex.common.views.onboarding.ReadableText
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.platform.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
enum class LAMode {
|
||||
SYSTEM,
|
||||
|
@ -119,15 +119,15 @@ fun PrivacySettingsView(
|
|||
chatModel.currentUser.value = currentUser.copy(sendRcptsContacts = enable)
|
||||
if (clearOverrides) {
|
||||
// For loop here is to prevent ConcurrentModificationException that happens with forEach
|
||||
withChats {
|
||||
for (i in 0 until chats.size) {
|
||||
val chat = chats[i]
|
||||
withContext(Dispatchers.Main) {
|
||||
for (i in 0 until chatModel.chatsContext.chats.size) {
|
||||
val chat = chatModel.chatsContext.chats[i]
|
||||
if (chat.chatInfo is ChatInfo.Direct) {
|
||||
var contact = chat.chatInfo.contact
|
||||
val sendRcpts = contact.chatSettings.sendRcpts
|
||||
if (sendRcpts != null && sendRcpts != enable) {
|
||||
contact = contact.copy(chatSettings = contact.chatSettings.copy(sendRcpts = null))
|
||||
updateContact(currentUser.remoteHostId, contact)
|
||||
chatModel.chatsContext.updateContact(currentUser.remoteHostId, contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,16 +143,16 @@ fun PrivacySettingsView(
|
|||
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
||||
chatModel.currentUser.value = currentUser.copy(sendRcptsSmallGroups = enable)
|
||||
if (clearOverrides) {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
// For loop here is to prevent ConcurrentModificationException that happens with forEach
|
||||
for (i in 0 until chats.size) {
|
||||
val chat = chats[i]
|
||||
for (i in 0 until chatModel.chatsContext.chats.size) {
|
||||
val chat = chatModel.chatsContext.chats[i]
|
||||
if (chat.chatInfo is ChatInfo.Group) {
|
||||
var groupInfo = chat.chatInfo.groupInfo
|
||||
val sendRcpts = groupInfo.chatSettings.sendRcpts
|
||||
if (sendRcpts != null && sendRcpts != enable) {
|
||||
groupInfo = groupInfo.copy(chatSettings = groupInfo.chatSettings.copy(sendRcpts = null))
|
||||
updateGroup(currentUser.remoteHostId, groupInfo)
|
||||
chatModel.chatsContext.updateGroup(currentUser.remoteHostId, groupInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ import androidx.compose.ui.platform.LocalDensity
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
|
||||
import chat.simplex.common.ui.theme.SimpleXTheme
|
||||
|
@ -58,12 +56,14 @@ fun showApp() {
|
|||
} else {
|
||||
// The last possible cause that can be closed
|
||||
withApi {
|
||||
withChats {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatId.value = null
|
||||
chatItems.clearAndNotify()
|
||||
chatModel.chatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
chatItems.clearAndNotify()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ModalManager.end.hasModalOpen(ModalViewId.SECONDARY_CHAT)) {
|
||||
chatModel.secondaryChatsContext.chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue