mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 22:55:48 +00:00
multiplatform: multi send & forward api (#4745)
This commit is contained in:
@@ -846,15 +846,15 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiSendMessage(rh: Long?, type: ChatType, id: Long, file: CryptoFile? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? {
|
||||
val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc, live, ttl)
|
||||
suspend fun apiSendMessages(rh: Long?, type: ChatType, id: Long, live: Boolean = false, ttl: Int? = null, composedMessages: List<ComposedMessage>): List<AChatItem>? {
|
||||
val cmd = CC.ApiSendMessages(type, id, live, ttl, composedMessages)
|
||||
return processSendMessageCmd(rh, cmd)
|
||||
}
|
||||
|
||||
private suspend fun processSendMessageCmd(rh: Long?, cmd: CC): AChatItem? {
|
||||
private suspend fun processSendMessageCmd(rh: Long?, cmd: CC): List<AChatItem>? {
|
||||
val r = sendCmd(rh, cmd)
|
||||
return when (r) {
|
||||
is CR.NewChatItem -> r.chatItem
|
||||
is CR.NewChatItems -> r.chatItems
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("processSendMessageCmd", generalGetString(MR.strings.error_sending_message), r)
|
||||
@@ -863,13 +863,13 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
}
|
||||
suspend fun apiCreateChatItem(rh: Long?, noteFolderId: Long, file: CryptoFile? = null, mc: MsgContent): AChatItem? {
|
||||
val cmd = CC.ApiCreateChatItem(noteFolderId, file, mc)
|
||||
suspend fun apiCreateChatItems(rh: Long?, noteFolderId: Long, composedMessages: List<ComposedMessage>): List<AChatItem>? {
|
||||
val cmd = CC.ApiCreateChatItems(noteFolderId, composedMessages)
|
||||
val r = sendCmd(rh, cmd)
|
||||
return when (r) {
|
||||
is CR.NewChatItem -> r.chatItem
|
||||
is CR.NewChatItems -> r.chatItems
|
||||
else -> {
|
||||
apiErrorAlert("apiCreateChatItem", generalGetString(MR.strings.error_creating_message), r)
|
||||
apiErrorAlert("apiCreateChatItems", generalGetString(MR.strings.error_creating_message), r)
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -885,9 +885,9 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiForwardChatItem(rh: Long?, toChatType: ChatType, toChatId: Long, fromChatType: ChatType, fromChatId: Long, itemId: Long, ttl: Int?): ChatItem? {
|
||||
val cmd = CC.ApiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId, ttl)
|
||||
return processSendMessageCmd(rh, cmd)?.chatItem
|
||||
suspend fun apiForwardChatItems(rh: Long?, toChatType: ChatType, toChatId: Long, fromChatType: ChatType, fromChatId: Long, itemIds: List<Long>, ttl: Int?): List<ChatItem>? {
|
||||
val cmd = CC.ApiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl)
|
||||
return processSendMessageCmd(rh, cmd)?.map { it.chatItem }
|
||||
}
|
||||
|
||||
|
||||
@@ -2132,27 +2132,30 @@ object ChatController {
|
||||
chatModel.networkStatuses[s.agentConnId] = s.networkStatus
|
||||
}
|
||||
}
|
||||
is CR.NewChatItem -> withBGApi {
|
||||
val cInfo = r.chatItem.chatInfo
|
||||
val cItem = r.chatItem.chatItem
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
addChatItem(rhId, cInfo, cItem)
|
||||
is CR.NewChatItems -> withBGApi {
|
||||
r.chatItems.forEach { chatItem ->
|
||||
val cInfo = chatItem.chatInfo
|
||||
val cItem = chatItem.chatItem
|
||||
if (active(r.user)) {
|
||||
withChats {
|
||||
addChatItem(rhId, cInfo, cItem)
|
||||
}
|
||||
} else if (cItem.isRcvNew && cInfo.ntfsEnabled) {
|
||||
chatModel.increaseUnreadCounter(rhId, r.user)
|
||||
}
|
||||
} else if (cItem.isRcvNew && cInfo.ntfsEnabled) {
|
||||
chatModel.increaseUnreadCounter(rhId, r.user)
|
||||
}
|
||||
val file = cItem.file
|
||||
val mc = cItem.content.msgContent
|
||||
if (file != null &&
|
||||
val file = cItem.file
|
||||
val mc = cItem.content.msgContent
|
||||
if (file != null &&
|
||||
appPrefs.privacyAcceptImages.get() &&
|
||||
((mc is MsgContent.MCImage && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV)
|
||||
|| (mc is MsgContent.MCVideo && file.fileSize <= MAX_VIDEO_SIZE_AUTO_RCV)
|
||||
|| (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileStatus !is CIFileStatus.RcvAccepted))) {
|
||||
receiveFile(rhId, r.user, file.fileId, auto = true)
|
||||
}
|
||||
if (cItem.showNotification && (allowedToShowNotification() || chatModel.chatId.value != cInfo.id || chatModel.remoteHostId() != rhId)) {
|
||||
ntfManager.notifyMessageReceived(r.user, cInfo, cItem)
|
||||
|| (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileStatus !is CIFileStatus.RcvAccepted))
|
||||
) {
|
||||
receiveFile(rhId, r.user, file.fileId, auto = true)
|
||||
}
|
||||
if (cItem.showNotification && (allowedToShowNotification() || chatModel.chatId.value != cInfo.id || chatModel.remoteHostId() != rhId)) {
|
||||
ntfManager.notifyMessageReceived(r.user, cInfo, cItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
is CR.ChatItemStatusUpdated -> {
|
||||
@@ -2863,13 +2866,13 @@ sealed class CC {
|
||||
class ApiGetChats(val userId: Long): CC()
|
||||
class ApiGetChat(val type: ChatType, val id: Long, val pagination: ChatPagination, val search: String = ""): CC()
|
||||
class ApiGetChatItemInfo(val type: ChatType, val id: Long, val itemId: Long): CC()
|
||||
class ApiSendMessage(val type: ChatType, val id: Long, val file: CryptoFile?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean, val ttl: Int?): CC()
|
||||
class ApiCreateChatItem(val noteFolderId: Long, val file: CryptoFile?, val mc: MsgContent): CC()
|
||||
class ApiSendMessages(val type: ChatType, val id: Long, val live: Boolean, val ttl: Int?, val composedMessages: List<ComposedMessage>): CC()
|
||||
class ApiCreateChatItems(val noteFolderId: Long, val composedMessages: List<ComposedMessage>): CC()
|
||||
class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC()
|
||||
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemIds: List<Long>, val mode: CIDeleteMode): CC()
|
||||
class ApiDeleteMemberChatItem(val groupId: Long, val itemIds: List<Long>): CC()
|
||||
class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC()
|
||||
class ApiForwardChatItem(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemId: Long, val ttl: Int?): CC()
|
||||
class ApiForwardChatItems(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemIds: List<Long>, val ttl: Int?): CC()
|
||||
class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC()
|
||||
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiJoinGroup(val groupId: Long): CC()
|
||||
@@ -3008,20 +3011,22 @@ sealed class CC {
|
||||
is ApiGetChats -> "/_get chats $userId pcc=on"
|
||||
is ApiGetChat -> "/_get chat ${chatRef(type, id)} ${pagination.cmdString}" + (if (search == "") "" else " search=$search")
|
||||
is ApiGetChatItemInfo -> "/_get item info ${chatRef(type, id)} $itemId"
|
||||
is ApiSendMessage -> {
|
||||
is ApiSendMessages -> {
|
||||
val msgs = json.encodeToString(composedMessages)
|
||||
val ttlStr = if (ttl != null) "$ttl" else "default"
|
||||
"/_send ${chatRef(type, id)} live=${onOff(live)} ttl=${ttlStr} json ${json.encodeToString(ComposedMessage(file, quotedItemId, mc))}"
|
||||
"/_send ${chatRef(type, id)} live=${onOff(live)} ttl=${ttlStr} json $msgs"
|
||||
}
|
||||
is ApiCreateChatItem -> {
|
||||
"/_create *$noteFolderId json ${json.encodeToString(ComposedMessage(file, null, mc))}"
|
||||
is ApiCreateChatItems -> {
|
||||
val msgs = json.encodeToString(composedMessages)
|
||||
"/_create *$noteFolderId json $msgs"
|
||||
}
|
||||
is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${mc.cmdString}"
|
||||
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} ${itemIds.joinToString(",")} ${mode.deleteMode}"
|
||||
is ApiDeleteMemberChatItem -> "/_delete member item #$groupId ${itemIds.joinToString(",")}"
|
||||
is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}"
|
||||
is ApiForwardChatItem -> {
|
||||
is ApiForwardChatItems -> {
|
||||
val ttlStr = if (ttl != null) "$ttl" else "default"
|
||||
"/_forward ${chatRef(toChatType, toChatId)} ${chatRef(fromChatType, fromChatId)} $itemId ttl=${ttlStr}"
|
||||
"/_forward ${chatRef(toChatType, toChatId)} ${chatRef(fromChatType, fromChatId)} ${itemIds.joinToString(",")} ttl=${ttlStr}"
|
||||
}
|
||||
is ApiNewGroup -> "/_group $userId incognito=${onOff(incognito)} ${json.encodeToString(groupProfile)}"
|
||||
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
|
||||
@@ -3158,13 +3163,13 @@ sealed class CC {
|
||||
is ApiGetChats -> "apiGetChats"
|
||||
is ApiGetChat -> "apiGetChat"
|
||||
is ApiGetChatItemInfo -> "apiGetChatItemInfo"
|
||||
is ApiSendMessage -> "apiSendMessage"
|
||||
is ApiCreateChatItem -> "apiCreateChatItem"
|
||||
is ApiSendMessages -> "apiSendMessages"
|
||||
is ApiCreateChatItems -> "apiCreateChatItems"
|
||||
is ApiUpdateChatItem -> "apiUpdateChatItem"
|
||||
is ApiDeleteChatItem -> "apiDeleteChatItem"
|
||||
is ApiDeleteMemberChatItem -> "apiDeleteMemberChatItem"
|
||||
is ApiChatItemReaction -> "apiChatItemReaction"
|
||||
is ApiForwardChatItem -> "apiForwardChatItem"
|
||||
is ApiForwardChatItems -> "apiForwardChatItems"
|
||||
is ApiNewGroup -> "apiNewGroup"
|
||||
is ApiAddMember -> "apiAddMember"
|
||||
is ApiJoinGroup -> "apiJoinGroup"
|
||||
@@ -4790,7 +4795,7 @@ sealed class 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()
|
||||
@Serializable @SerialName("newChatItem") class NewChatItem(val user: UserRef, val chatItem: AChatItem): CR()
|
||||
@Serializable @SerialName("newChatItems") class NewChatItems(val user: UserRef, val chatItems: List<AChatItem>): CR()
|
||||
@Serializable @SerialName("chatItemStatusUpdated") class ChatItemStatusUpdated(val user: UserRef, val chatItem: AChatItem): CR()
|
||||
@Serializable @SerialName("chatItemUpdated") class ChatItemUpdated(val user: UserRef, val chatItem: AChatItem): CR()
|
||||
@Serializable @SerialName("chatItemNotChanged") class ChatItemNotChanged(val user: UserRef, val chatItem: AChatItem): CR()
|
||||
@@ -4966,7 +4971,7 @@ sealed class CR {
|
||||
is MemberSubErrors -> "memberSubErrors"
|
||||
is GroupEmpty -> "groupEmpty"
|
||||
is UserContactLinkSubscribed -> "userContactLinkSubscribed"
|
||||
is NewChatItem -> "newChatItem"
|
||||
is NewChatItems -> "newChatItems"
|
||||
is ChatItemStatusUpdated -> "chatItemStatusUpdated"
|
||||
is ChatItemUpdated -> "chatItemUpdated"
|
||||
is ChatItemNotChanged -> "chatItemNotChanged"
|
||||
@@ -5134,7 +5139,7 @@ sealed class CR {
|
||||
is MemberSubErrors -> withUser(user, json.encodeToString(memberSubErrors))
|
||||
is GroupEmpty -> withUser(user, json.encodeToString(group))
|
||||
is UserContactLinkSubscribed -> noDetails()
|
||||
is NewChatItem -> withUser(user, json.encodeToString(chatItem))
|
||||
is NewChatItems -> withUser(user, chatItems.joinToString("\n") { json.encodeToString(it) })
|
||||
is ChatItemStatusUpdated -> withUser(user, json.encodeToString(chatItem))
|
||||
is ChatItemUpdated -> withUser(user, json.encodeToString(chatItem))
|
||||
is ChatItemNotChanged -> withUser(user, json.encodeToString(chatItem))
|
||||
|
||||
@@ -380,24 +380,28 @@ fun ComposeView(
|
||||
|
||||
suspend fun send(chat: Chat, mc: MsgContent, quoted: Long?, file: CryptoFile? = null, live: Boolean = false, ttl: Int?): ChatItem? {
|
||||
val cInfo = chat.chatInfo
|
||||
val aChatItem = if (chat.chatInfo.chatType == ChatType.Local)
|
||||
chatModel.controller.apiCreateChatItem(rh = chat.remoteHostId, noteFolderId = chat.chatInfo.apiId, file = file, mc = mc)
|
||||
val chatItems = if (chat.chatInfo.chatType == ChatType.Local)
|
||||
chatModel.controller.apiCreateChatItems(
|
||||
rh = chat.remoteHostId,
|
||||
noteFolderId = chat.chatInfo.apiId,
|
||||
composedMessages = listOf(ComposedMessage(file, null, mc))
|
||||
)
|
||||
else
|
||||
chatModel.controller.apiSendMessage(
|
||||
rh = chat.remoteHostId,
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
file = file,
|
||||
quotedItemId = quoted,
|
||||
mc = mc,
|
||||
live = live,
|
||||
ttl = ttl
|
||||
)
|
||||
if (aChatItem != null) {
|
||||
withChats {
|
||||
addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
|
||||
chatModel.controller.apiSendMessages(
|
||||
rh = chat.remoteHostId,
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
live = live,
|
||||
ttl = ttl,
|
||||
composedMessages = listOf(ComposedMessage(file, quoted, mc))
|
||||
)
|
||||
if (!chatItems.isNullOrEmpty()) {
|
||||
chatItems.forEach { aChatItem ->
|
||||
withChats {
|
||||
addChatItem(chat.remoteHostId, cInfo, aChatItem.chatItem)
|
||||
}
|
||||
}
|
||||
return aChatItem.chatItem
|
||||
return chatItems.first().chatItem
|
||||
}
|
||||
if (file != null) removeFile(file.filePath)
|
||||
return null
|
||||
@@ -414,21 +418,22 @@ fun ComposeView(
|
||||
}
|
||||
|
||||
suspend fun forwardItem(rhId: Long?, forwardedItem: ChatItem, fromChatInfo: ChatInfo, ttl: Int?): ChatItem? {
|
||||
val chatItem = controller.apiForwardChatItem(
|
||||
val chatItems = controller.apiForwardChatItems(
|
||||
rh = rhId,
|
||||
toChatType = chat.chatInfo.chatType,
|
||||
toChatId = chat.chatInfo.apiId,
|
||||
fromChatType = fromChatInfo.chatType,
|
||||
fromChatId = fromChatInfo.apiId,
|
||||
itemId = forwardedItem.id,
|
||||
itemIds = listOf(forwardedItem.id),
|
||||
ttl = ttl
|
||||
)
|
||||
if (chatItem != null) {
|
||||
chatItems?.forEach { chatItem ->
|
||||
withChats {
|
||||
addChatItem(rhId, chat.chatInfo, chatItem)
|
||||
}
|
||||
}
|
||||
return chatItem
|
||||
// TODO batch send: forward multiple messages
|
||||
return chatItems?.firstOrNull()
|
||||
}
|
||||
|
||||
fun checkLinkPreview(): MsgContent {
|
||||
@@ -519,6 +524,7 @@ fun ComposeView(
|
||||
ComposePreview.NoPreview -> msgs.add(MsgContent.MCText(msgText))
|
||||
is ComposePreview.CLinkPreview -> msgs.add(checkLinkPreview())
|
||||
is ComposePreview.MediaPreview -> {
|
||||
// TODO batch send: batch media previews
|
||||
preview.content.forEachIndexed { index, it ->
|
||||
val file = when (it) {
|
||||
is UploadContent.SimpleImage ->
|
||||
|
||||
Reference in New Issue
Block a user