mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-06-03 19:11:37 +00:00
Merge branch 'master' into master-ghc8107
This commit is contained in:
+46
-25
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
+66
-18
@@ -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))
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+6
-2
@@ -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}")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user