Merge branch 'master' into master-ghc8107

This commit is contained in:
Evgeny Poberezkin
2023-10-15 18:53:23 +01:00
30 changed files with 995 additions and 339 deletions
@@ -6,17 +6,17 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.style.TextDecoration
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.call.*
import chat.simplex.common.views.chat.ComposeState
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.onboarding.OnboardingStage
import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource
import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.*
import kotlinx.datetime.TimeZone
import kotlinx.serialization.*
@@ -103,6 +103,8 @@ object ChatModel {
val filesToDelete = mutableSetOf<File>()
val simplexLinkMode by lazy { mutableStateOf(ChatController.appPrefs.simplexLinkMode.get()) }
var updatingChatsMutex: Mutex = Mutex()
fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) {
currentUser.value
} else {
@@ -199,7 +201,7 @@ object ChatModel {
}
}
suspend fun addChatItem(cInfo: ChatInfo, cItem: ChatItem) {
suspend fun addChatItem(cInfo: ChatInfo, cItem: ChatItem) = updatingChatsMutex.withLock {
// update previews
val i = getChatIndex(cInfo.id)
val chat: Chat
@@ -222,10 +224,11 @@ object ChatModel {
} else {
addChat(Chat(chatInfo = cInfo, chatItems = arrayListOf(cItem)))
}
// add to current chat
if (chatId.value == cInfo.id) {
Log.d(TAG, "TODOCHAT: addChatItem: adding to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
withContext(Dispatchers.Main) {
Log.d(TAG, "TODOCHAT: addChatItem: adding to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
withContext(Dispatchers.Main) {
// add to current chat
if (chatId.value == cInfo.id) {
Log.d(TAG, "TODOCHAT: addChatItem: chatIds are equal, size ${chatItems.size}")
// Prevent situation when chat item already in the list received from backend
if (chatItems.none { it.id == cItem.id }) {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
@@ -239,7 +242,7 @@ object ChatModel {
}
}
suspend fun upsertChatItem(cInfo: ChatInfo, cItem: ChatItem): Boolean {
suspend fun upsertChatItem(cInfo: ChatInfo, cItem: ChatItem): Boolean = updatingChatsMutex.withLock {
// update previews
val i = getChatIndex(cInfo.id)
val chat: Chat
@@ -259,10 +262,10 @@ object ChatModel {
addChat(Chat(chatInfo = cInfo, chatItems = arrayListOf(cItem)))
res = true
}
// update current chat
return if (chatId.value == cInfo.id) {
Log.d(TAG, "TODOCHAT: upsertChatItem: upserting to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
withContext(Dispatchers.Main) {
Log.d(TAG, "TODOCHAT: upsertChatItem: upserting to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
return withContext(Dispatchers.Main) {
// update current chat
if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
chatItems[itemIndex] = cItem
@@ -273,15 +276,15 @@ object ChatModel {
Log.d(TAG, "TODOCHAT: upsertChatItem: added to chat $chatId from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
true
}
} else {
res
}
} else {
res
}
}
suspend fun updateChatItem(cInfo: ChatInfo, cItem: ChatItem) {
if (chatId.value == cInfo.id) {
withContext(Dispatchers.Main) {
withContext(Dispatchers.Main) {
if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
chatItems[itemIndex] = cItem
@@ -726,7 +729,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val apiId get() = contactConnection.apiId
override val ready get() = contactConnection.ready
override val sendMsgEnabled get() = contactConnection.sendMsgEnabled
override val ntfsEnabled get() = contactConnection.incognito
override val ntfsEnabled get() = false
override val incognito get() = contactConnection.incognito
override fun featureEnabled(feature: ChatFeature) = contactConnection.featureEnabled(feature)
override val timedMessagesTTL: Int? get() = contactConnection.timedMessagesTTL
@@ -786,16 +789,19 @@ sealed class NetworkStatus {
val statusExplanation: String get() =
when (this) {
is Connected -> generalGetString(MR.strings.connected_to_server_to_receive_messages_from_contact)
is Error -> String.format(generalGetString(MR.strings.trying_to_connect_to_server_to_receive_messages_with_error), error)
is Error -> String.format(generalGetString(MR.strings.trying_to_connect_to_server_to_receive_messages_with_error), connectionError)
else -> generalGetString(MR.strings.trying_to_connect_to_server_to_receive_messages)
}
@Serializable @SerialName("unknown") class Unknown: NetworkStatus()
@Serializable @SerialName("connected") class Connected: NetworkStatus()
@Serializable @SerialName("disconnected") class Disconnected: NetworkStatus()
@Serializable @SerialName("error") class Error(val error: String): NetworkStatus()
@Serializable @SerialName("error") class Error(val connectionError: String): NetworkStatus()
}
@Serializable
data class ConnNetworkStatus(val agentConnId: String, val networkStatus: NetworkStatus)
@Serializable
data class Contact(
val contactId: Long,
@@ -822,7 +828,7 @@ data class Contact(
(ready && active && !(activeConn.connectionStats?.ratchetSyncSendProhibited ?: false))
|| nextSendGrpInv
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
override val ntfsEnabled get() = chatSettings.enableNtfs
override val ntfsEnabled get() = chatSettings.enableNtfs == MsgFilter.All
override val incognito get() = contactConnIncognito
override fun featureEnabled(feature: ChatFeature) = when (feature) {
ChatFeature.TimedMessages -> mergedPreferences.timedMessages.enabled.forUser
@@ -869,7 +875,7 @@ data class Contact(
activeConn = Connection.sampleData,
contactUsed = true,
contactStatus = ContactStatus.Active,
chatSettings = ChatSettings(enableNtfs = true, sendRcpts = null, favorite = false),
chatSettings = ChatSettings(enableNtfs = MsgFilter.All, sendRcpts = null, favorite = false),
userPreferences = ChatPreferences.sampleData,
mergedPreferences = ContactUserPreferences.sampleData,
createdAt = Clock.System.now(),
@@ -1009,7 +1015,7 @@ data class GroupInfo (
override val apiId get() = groupId
override val ready get() = membership.memberActive
override val sendMsgEnabled get() = membership.memberActive
override val ntfsEnabled get() = chatSettings.enableNtfs
override val ntfsEnabled get() = chatSettings.enableNtfs == MsgFilter.All
override val incognito get() = membership.memberIncognito
override fun featureEnabled(feature: ChatFeature) = when (feature) {
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
@@ -1041,13 +1047,16 @@ data class GroupInfo (
fullGroupPreferences = FullGroupPreferences.sampleData,
membership = GroupMember.sampleData,
hostConnCustomUserProfileId = null,
chatSettings = ChatSettings(enableNtfs = true, sendRcpts = null, favorite = false),
chatSettings = ChatSettings(enableNtfs = MsgFilter.All, sendRcpts = null, favorite = false),
createdAt = Clock.System.now(),
updatedAt = Clock.System.now()
)
}
}
@Serializable
data class GroupRef(val groupId: Long, val localDisplayName: String)
@Serializable
data class GroupProfile (
override val displayName: String,
@@ -1073,6 +1082,7 @@ data class GroupMember (
var memberRole: GroupMemberRole,
var memberCategory: GroupMemberCategory,
var memberStatus: GroupMemberStatus,
var memberSettings: GroupMemberSettings,
var invitedBy: InvitedBy,
val localDisplayName: String,
val memberProfile: LocalProfile,
@@ -1140,6 +1150,7 @@ data class GroupMember (
memberRole = GroupMemberRole.Member,
memberCategory = GroupMemberCategory.InviteeMember,
memberStatus = GroupMemberStatus.MemComplete,
memberSettings = GroupMemberSettings(showMessages = true),
invitedBy = InvitedBy.IBUser(),
localDisplayName = "alice",
memberProfile = LocalProfile.sampleData,
@@ -1151,11 +1162,20 @@ data class GroupMember (
}
@Serializable
class GroupMemberRef(
data class GroupMemberSettings(val showMessages: Boolean) {}
@Serializable
data class GroupMemberRef(
val groupMemberId: Long,
val profile: Profile
)
@Serializable
data class GroupMemberIds(
val groupMemberId: Long,
val groupId: Long
)
@Serializable
enum class GroupMemberRole(val memberRole: String) {
@SerialName("observer") Observer("observer"), // order matters in comparisons
@@ -1249,7 +1269,7 @@ class LinkPreview (
@Serializable
class MemberSubError (
val member: GroupMember,
val member: GroupMemberIds,
val memberError: ChatError
)
@@ -1844,6 +1864,7 @@ enum class SndCIStatusProgress {
@Serializable
sealed class CIDeleted {
@Serializable @SerialName("deleted") class Deleted(val deletedTs: Instant?): CIDeleted()
@Serializable @SerialName("blocked") class Blocked(val deletedTs: Instant?): CIDeleted()
@Serializable @SerialName("moderated") class Moderated(val deletedTs: Instant?, val byGroupMember: GroupMember): CIDeleted()
}
@@ -4,6 +4,7 @@ import chat.simplex.common.views.helpers.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import chat.simplex.common.model.ChatModel.updatingChatsMutex
import dev.icerock.moko.resources.compose.painterResource
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
@@ -16,6 +17,7 @@ import com.charleskorn.kaml.YamlConfiguration
import chat.simplex.res.MR
import com.russhwolf.settings.Settings
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.*
@@ -336,6 +338,7 @@ object ChatController {
apiSetTempFolder(coreTmpDir.absolutePath)
apiSetFilesFolder(appFilesDir.absolutePath)
apiSetXFTPConfig(getXFTPCfg())
// apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get())
val justStarted = apiStartChat()
val users = listUsers()
chatModel.users.clear()
@@ -349,8 +352,10 @@ object ChatController {
startReceiver()
Log.d(TAG, "startChat: started")
} else {
val chats = apiGetChats()
chatModel.updateChats(chats)
updatingChatsMutex.withLock {
val chats = apiGetChats()
chatModel.updateChats(chats)
}
Log.d(TAG, "startChat: running")
}
} catch (e: Error) {
@@ -384,8 +389,10 @@ object ChatController {
suspend fun getUserChatData() {
chatModel.userAddress.value = apiGetUserAddress()
chatModel.chatItemTTL.value = getChatItemTTL()
val chats = apiGetChats()
chatModel.updateChats(chats)
updatingChatsMutex.withLock {
val chats = apiGetChats()
chatModel.updateChats(chats)
}
}
private fun startReceiver() {
@@ -553,6 +560,8 @@ object ChatController {
throw Error("apiSetXFTPConfig bad response: ${r.responseType} ${r.details}")
}
suspend fun apiSetEncryptLocalFiles(enable: Boolean) = sendCommandOkResp(CC.ApiSetEncryptLocalFiles(enable))
suspend fun apiExportArchive(config: ArchiveConfig) {
val r = sendCmd(CC.ApiExportArchive(config))
if (r is CR.CmdOk) return
@@ -1076,6 +1085,13 @@ object ChatController {
return r is CR.CmdOk
}
suspend fun apiGetNetworkStatuses(): List<ConnNetworkStatus>? {
val r = sendCmd(CC.ApiGetNetworkStatuses())
if (r is CR.NetworkStatuses) return r.networkStatuses
Log.e(TAG, "apiGetNetworkStatuses bad response: ${r.responseType} ${r.details}")
return null
}
suspend fun apiChatRead(type: ChatType, id: Long, range: CC.ItemRange): Boolean {
val r = sendCmd(CC.ApiChatRead(type, id, range))
if (r is CR.CmdOk) return true
@@ -1314,6 +1330,13 @@ object ChatController {
}
}
private suspend fun sendCommandOkResp(cmd: CC): Boolean {
val r = sendCmd(cmd)
val ok = r is CR.CmdOk
if (!ok) apiErrorAlert(cmd.cmdType, generalGetString(MR.strings.error), r)
return ok
}
suspend fun apiGetVersion(): CoreVersionInfo? {
val r = sendCmd(CC.ShowVersion())
return if (r is CR.VersionInfo) {
@@ -1419,12 +1442,6 @@ object ChatController {
}
is CR.ContactsSubscribed -> updateContactsStatus(r.contactRefs, NetworkStatus.Connected())
is CR.ContactsDisconnected -> updateContactsStatus(r.contactRefs, NetworkStatus.Disconnected())
is CR.ContactSubError -> {
if (active(r.user)) {
chatModel.updateContact(r.contact)
}
processContactSubError(r.contact, r.chatError)
}
is CR.ContactSubSummary -> {
for (sub in r.contactSubscriptions) {
if (active(r.user)) {
@@ -1438,6 +1455,16 @@ object ChatController {
}
}
}
is CR.NetworkStatusResp -> {
for (cId in r.connections) {
chatModel.networkStatuses[cId] = r.networkStatus
}
}
is CR.NetworkStatuses -> {
for (s in r.networkStatuses) {
chatModel.networkStatuses[s.agentConnId] = s.networkStatus
}
}
is CR.NewChatItem -> {
val cInfo = r.chatItem.chatInfo
val cItem = r.chatItem.chatItem
@@ -1841,6 +1868,7 @@ sealed class CC {
class SetTempFolder(val tempFolder: String): CC()
class SetFilesFolder(val filesFolder: String): CC()
class ApiSetXFTPConfig(val config: XFTPFileConfig?): CC()
class ApiSetEncryptLocalFiles(val enable: Boolean): CC()
class ApiExportArchive(val config: ArchiveConfig): CC()
class ApiImportArchive(val config: ArchiveConfig): CC()
class ApiDeleteStorage: CC()
@@ -1909,11 +1937,12 @@ sealed class CC {
class ApiSendCallExtraInfo(val contact: Contact, val extraInfo: WebRTCExtraInfo): CC()
class ApiEndCall(val contact: Contact): CC()
class ApiCallStatus(val contact: Contact, val callStatus: WebRTCCallStatus): CC()
class ApiGetNetworkStatuses(): CC()
class ApiAcceptContact(val incognito: Boolean, val contactReqId: Long): CC()
class ApiRejectContact(val contactReqId: Long): CC()
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
class ReceiveFile(val fileId: Long, val encrypted: Boolean, val inline: Boolean?): CC()
class ReceiveFile(val fileId: Long, val encrypt: Boolean?, val inline: Boolean?): CC()
class CancelFile(val fileId: Long): CC()
class ShowVersion(): CC()
@@ -1945,6 +1974,7 @@ sealed class CC {
is SetTempFolder -> "/_temp_folder $tempFolder"
is SetFilesFolder -> "/_files_folder $filesFolder"
is ApiSetXFTPConfig -> if (config != null) "/_xftp on ${json.encodeToString(config)}" else "/_xftp off"
is ApiSetEncryptLocalFiles -> "/_files_encrypt ${onOff(enable)}"
is ApiExportArchive -> "/_db export ${json.encodeToString(config)}"
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
is ApiDeleteStorage -> "/_db delete"
@@ -2018,9 +2048,13 @@ sealed class CC {
is ApiSendCallExtraInfo -> "/_call extra @${contact.apiId} ${json.encodeToString(extraInfo)}"
is ApiEndCall -> "/_call end @${contact.apiId}"
is ApiCallStatus -> "/_call status @${contact.apiId} ${callStatus.value}"
is ApiGetNetworkStatuses -> "/_network_statuses"
is ApiChatRead -> "/_read chat ${chatRef(type, id)} from=${range.from} to=${range.to}"
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
is ReceiveFile -> "/freceive $fileId encrypt=${onOff(encrypted)}" + (if (inline == null) "" else " inline=${onOff(inline)}")
is ReceiveFile ->
"/freceive $fileId" +
(if (encrypt == null) "" else " encrypt=${onOff(encrypt)}") +
(if (inline == null) "" else " inline=${onOff(inline)}")
is CancelFile -> "/fcancel $fileId"
is ShowVersion -> "/version"
}
@@ -2044,6 +2078,7 @@ sealed class CC {
is SetTempFolder -> "setTempFolder"
is SetFilesFolder -> "setFilesFolder"
is ApiSetXFTPConfig -> "apiSetXFTPConfig"
is ApiSetEncryptLocalFiles -> "apiSetEncryptLocalFiles"
is ApiExportArchive -> "apiExportArchive"
is ApiImportArchive -> "apiImportArchive"
is ApiDeleteStorage -> "apiDeleteStorage"
@@ -2114,6 +2149,7 @@ sealed class CC {
is ApiSendCallExtraInfo -> "apiSendCallExtraInfo"
is ApiEndCall -> "apiEndCall"
is ApiCallStatus -> "apiCallStatus"
is ApiGetNetworkStatuses -> "apiGetNetworkStatuses"
is ApiChatRead -> "apiChatRead"
is ApiChatUnread -> "apiChatUnread"
is ReceiveFile -> "receiveFile"
@@ -2472,15 +2508,22 @@ data class KeepAliveOpts(
@Serializable
data class ChatSettings(
val enableNtfs: Boolean,
val enableNtfs: MsgFilter,
val sendRcpts: Boolean?,
val favorite: Boolean
) {
companion object {
val defaults: ChatSettings = ChatSettings(enableNtfs = true, sendRcpts = null, favorite = false)
val defaults: ChatSettings = ChatSettings(enableNtfs = MsgFilter.All, sendRcpts = null, favorite = false)
}
}
@Serializable
enum class MsgFilter {
@SerialName("all") All,
@SerialName("none") None,
@SerialName("mentions") Mentions,
}
@Serializable
data class UserMsgReceiptSettings(val enable: Boolean, val clearOverrides: Boolean)
@@ -3320,11 +3363,14 @@ sealed class CR {
@Serializable @SerialName("acceptingContactRequest") class AcceptingContactRequest(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactRequestRejected") class ContactRequestRejected(val user: UserRef): CR()
@Serializable @SerialName("contactUpdated") class ContactUpdated(val user: UserRef, val toContact: Contact): CR()
// TODO remove below
@Serializable @SerialName("contactsSubscribed") class ContactsSubscribed(val server: String, val contactRefs: List<ContactRef>): CR()
@Serializable @SerialName("contactsDisconnected") class ContactsDisconnected(val server: String, val contactRefs: List<ContactRef>): CR()
@Serializable @SerialName("contactSubError") class ContactSubError(val user: UserRef, val contact: Contact, val chatError: ChatError): CR()
@Serializable @SerialName("contactSubSummary") class ContactSubSummary(val user: UserRef, val contactSubscriptions: List<ContactSubStatus>): CR()
@Serializable @SerialName("groupSubscribed") class GroupSubscribed(val user: UserRef, val group: GroupInfo): CR()
// TODO remove above
@Serializable @SerialName("networkStatus") class NetworkStatusResp(val networkStatus: NetworkStatus, val connections: List<String>): CR()
@Serializable @SerialName("networkStatuses") class NetworkStatuses(val user_: UserRef?, val networkStatuses: List<ConnNetworkStatus>): CR()
@Serializable @SerialName("groupSubscribed") class GroupSubscribed(val user: UserRef, val group: GroupRef): CR()
@Serializable @SerialName("memberSubErrors") class MemberSubErrors(val user: UserRef, val memberSubErrors: List<MemberSubError>): CR()
@Serializable @SerialName("groupEmpty") class GroupEmpty(val user: UserRef, val group: GroupInfo): CR()
@Serializable @SerialName("userContactLinkSubscribed") class UserContactLinkSubscribed: CR()
@@ -3454,8 +3500,9 @@ sealed class CR {
is ContactUpdated -> "contactUpdated"
is ContactsSubscribed -> "contactsSubscribed"
is ContactsDisconnected -> "contactsDisconnected"
is ContactSubError -> "contactSubError"
is ContactSubSummary -> "contactSubSummary"
is NetworkStatusResp -> "networkStatus"
is NetworkStatuses -> "networkStatuses"
is GroupSubscribed -> "groupSubscribed"
is MemberSubErrors -> "memberSubErrors"
is GroupEmpty -> "groupEmpty"
@@ -3583,8 +3630,9 @@ sealed class CR {
is ContactUpdated -> withUser(user, json.encodeToString(toContact))
is ContactsSubscribed -> "server: $server\ncontacts:\n${json.encodeToString(contactRefs)}"
is ContactsDisconnected -> "server: $server\ncontacts:\n${json.encodeToString(contactRefs)}"
is ContactSubError -> withUser(user, "error:\n${chatError.string}\ncontact:\n${json.encodeToString(contact)}")
is ContactSubSummary -> withUser(user, json.encodeToString(contactSubscriptions))
is NetworkStatusResp -> "networkStatus $networkStatus\nconnections: $connections"
is NetworkStatuses -> withUser(user_, json.encodeToString(networkStatuses))
is GroupSubscribed -> withUser(user, json.encodeToString(group))
is MemberSubErrors -> withUser(user, json.encodeToString(memberSubErrors))
is GroupEmpty -> withUser(user, json.encodeToString(group))
@@ -595,8 +595,8 @@ fun groupInvitationAcceptedAlert() {
)
}
fun toggleNotifications(chat: Chat, enableNtfs: Boolean, chatModel: ChatModel, currentState: MutableState<Boolean>? = null) {
val chatSettings = (chat.chatInfo.chatSettings ?: ChatSettings.defaults).copy(enableNtfs = enableNtfs)
fun toggleNotifications(chat: Chat, enableAllNtfs: Boolean, chatModel: ChatModel, currentState: MutableState<Boolean>? = null) {
val chatSettings = (chat.chatInfo.chatSettings ?: ChatSettings.defaults).copy(enableNtfs = if (enableAllNtfs) MsgFilter.All else MsgFilter.None)
updateChatSettings(chat, chatSettings, chatModel, currentState)
}
@@ -627,7 +627,7 @@ fun updateChatSettings(chat: Chat, chatSettings: ChatSettings, chatModel: ChatMo
}
if (res && newChatInfo != null) {
chatModel.updateChatInfo(newChatInfo)
if (!chatSettings.enableNtfs) {
if (chatSettings.enableNtfs != MsgFilter.All) {
ntfManager.cancelNotificationsForChat(chat.id)
}
val current = currentState?.value
@@ -20,11 +20,13 @@ import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel.updatingChatsMutex
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.common.platform.*
import chat.simplex.res.MR
import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.*
import java.io.*
import java.net.URI
@@ -620,8 +622,10 @@ private fun afterSetCiTTL(
appFilesCountAndSize.value = directoryFileCountAndSize(appFilesDir.absolutePath)
withApi {
try {
val chats = m.controller.apiGetChats()
m.updateChats(chats)
updatingChatsMutex.withLock {
val chats = m.controller.apiGetChats()
m.updateChats(chats)
}
} catch (e: Exception) {
Log.e(TAG, "apiGetChats error: ${e.message}")
}