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