update model when messages arrive (#333)

* update model when messages arrive

* update chat in the list when message is added

* copy methods with optional parameters

* use data classes to have pre-defined copy methods
This commit is contained in:
Evgeny Poberezkin 2022-02-18 14:33:55 +00:00 committed by GitHub
parent f650308986
commit bba2783aa4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 219 additions and 9 deletions

View file

@ -12,6 +12,148 @@ class ChatModel(val controller: ChatController) {
var terminalItems = mutableStateListOf<TerminalItem>()
fun hasChat(id: String): Boolean = chats.firstOrNull() { it.id == id } != null
fun getChat(id: String): Chat? = chats.firstOrNull { it.id == id }
private fun getChatIndex(id: String): Int = chats.indexOfFirst { it.id == id }
fun addChat(chat: Chat) = chats.add(index = 0, chat)
// func updateChatInfo(_ cInfo: ChatInfo) {
// if let i = getChatIndex(cInfo.id) {
// chats[i].chatInfo = cInfo
// }
// }
//
// func updateContact(_ contact: Contact) {
// let cInfo = ChatInfo.direct(contact: contact)
// if hasChat(contact.id) {
// updateChatInfo(cInfo)
// } else {
// addChat(Chat(chatInfo: cInfo, chatItems: []))
// }
// }
//
// func updateNetworkStatus(_ contact: Contact, _ status: Chat.NetworkStatus) {
// if let ix = getChatIndex(contact.id) {
// chats[ix].serverInfo.networkStatus = status
// }
// }
//
// func replaceChat(_ id: String, _ chat: Chat) {
// if let i = getChatIndex(id) {
// chats[i] = chat
// } else {
// // invalid state, correcting
// chats.insert(chat, at: 0)
// }
// }
fun addChatItem(cInfo: ChatInfo, cItem: ChatItem) {
// update previews
val i = getChatIndex(cInfo.id)
if (i >= 0) {
val chat = chats[i]
val updatedChat = chat.copy(
chatItems = arrayListOf(cItem),
chatStats =
if (cItem.meta.itemStatus is CIStatus.RcvNew)
chat.chatStats.copy(unreadCount = chat.chatStats.unreadCount + 1)
else
chat.chatStats
)
chats.set(i, updatedChat)
if (i > 0) {
popChat_(i)
}
} else {
addChat(Chat(chatInfo = cInfo, chatItems = arrayListOf(cItem)))
}
// add to current chat
if (chatId.value == cInfo.id) {
chatItems.add(cItem)
if (cItem.meta.itemStatus is CIStatus.RcvNew) {
// TODO mark item read via api and model
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// if self.chatId == cInfo.id {
// SimpleX.markChatItemRead(cInfo, cItem)
// }
// }
}
}
}
//
// func upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
// // update previews
// var res: Bool
// if let chat = getChat(cInfo.id) {
// if let pItem = chat.chatItems.last, pItem.id == cItem.id {
// chat.chatItems = [cItem]
// }
// res = false
// } else {
// addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
// res = true
// }
// // update current chat
// if chatId == cInfo.id {
// if let i = chatItems.firstIndex(where: { $0.id == cItem.id }) {
// withAnimation(.default) {
// self.chatItems[i] = cItem
// }
// return false
// } else {
// withAnimation { chatItems.append(cItem) }
// return true
// }
// } else {
// return res
// }
// }
//
// func markChatItemsRead(_ cInfo: ChatInfo) {
// // update preview
// if let chat = getChat(cInfo.id) {
// chat.chatStats = ChatStats()
// }
// // update current chat
// if chatId == cInfo.id {
// var i = 0
// while i < chatItems.count {
// if case .rcvNew = chatItems[i].meta.itemStatus {
// chatItems[i].meta.itemStatus = .rcvRead
// }
// i = i + 1
// }
// }
// }
//
// func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) {
// // update preview
// if let i = getChatIndex(cInfo.id) {
// chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount - 1
// }
// // update current chat
// if chatId == cInfo.id, let j = chatItems.firstIndex(where: { $0.id == cItem.id }) {
// chatItems[j].meta.itemStatus = .rcvRead
// }
// }
//
// func popChat(_ id: String) {
// if let i = getChatIndex(id) {
// popChat_(i)
// }
// }
//
private fun popChat_(i: Int) {
val chat = chats.removeAt(i)
chats.add(index = 0, chat)
}
//
// func removeChat(_ id: String) {
// withAnimation {
// chats.removeAll(where: { $0.id == id })
// }
// }
companion object {
val sampleData: ChatModel get() {
val m = ChatModel(ChatController.Mock())
@ -70,17 +212,16 @@ interface SomeChat {
}
@Serializable
class Chat (
data class Chat (
val chatInfo: ChatInfo,
val chatItems: List<ChatItem>,
val chatStats: ChatStats,
val chatStats: ChatStats = ChatStats(),
val serverInfo: ServerInfo = ServerInfo(NetworkStatus.Unknown())
) {
val id: String get() = chatInfo.id
@Serializable
class ChatStats {
}
data class ChatStats(val unreadCount: Int = 0, val minUnreadItemId: Long = 0)
@Serializable
class ServerInfo(val networkStatus: NetworkStatus)

View file

@ -43,9 +43,10 @@ open class ChatController(val ctrl: ChatCtrl) {
// val chatlog = FifoQueue<String>(500)
while(true) {
val json = chatRecvMsg(ctrl)
Log.d("SIMPLEX chatRecvMsg: ", json)
val r = APIResponse.decodeStr(json)
chatModel.terminalItems.add(TerminalItem.resp(r.resp))
val r = APIResponse.decodeStr(json).resp
Log.d("SIMPLEX", "chatRecvMsg: ${r.responseType}")
if (r is CR.Response || r is CR.Invalid) Log.d("SIMPLEX", "chatRecvMsg json: $json")
processReceivedMsg(r)
}
}
}
@ -106,6 +107,71 @@ open class ChatController(val ctrl: ChatCtrl) {
return null
}
fun processReceivedMsg(r: CR) {
chatModel.terminalItems.add(TerminalItem.resp(r))
when {
// r is CR.ContactConnected -> return
// r is CR.UpdateNetworkStatus -> return
// r is CR.ReceivedContactRequest -> return
// r is CR.ContactUpdated -> return
// r is CR.ContactSubscribed -> return
// r is CR.ContactSubError -> return
// r is CR.UpdateContact -> return
r is CR.NewChatItem -> {
val cInfo = r.chatItem.chatInfo
val cItem = r.chatItem.chatItem
chatModel.addChatItem(cInfo, cItem)
}
// NtfManager.shared.notifyMessageReceived(cInfo, cItem)
// switch res {
// case let .contactConnected(contact):
// chatModel.updateContact(contact)
// chatModel.updateNetworkStatus(contact, .connected)
// NtfManager.shared.notifyContactConnected(contact)
// case let .receivedContactRequest(contactRequest):
// chatModel.addChat(Chat(
// chatInfo: ChatInfo.contactRequest(contactRequest: contactRequest),
// chatItems: []
// ))
// NtfManager.shared.notifyContactRequest(contactRequest)
// case let .contactUpdated(toContact):
// let cInfo = ChatInfo.direct(contact: toContact)
// if chatModel.hasChat(toContact.id) {
// chatModel.updateChatInfo(cInfo)
// }
// case let .contactSubscribed(contact):
// chatModel.updateContact(contact)
// chatModel.updateNetworkStatus(contact, .connected)
// case let .contactDisconnected(contact):
// chatModel.updateContact(contact)
// chatModel.updateNetworkStatus(contact, .disconnected)
// case let .contactSubError(contact, chatError):
// chatModel.updateContact(contact)
//// var err: String
//// switch chatError {
//// case .errorAgent(agentError: .BROKER(brokerErr: .NETWORK)): err = "network"
//// case .errorAgent(agentError: .SMP(smpErr: .AUTH)): err = "contact deleted"
//// default: err = String(describing: chatError)
//// }
//// chatModel.updateNetworkStatus(contact, .error(err))
// case let .newChatItem(aChatItem):
// let cInfo = aChatItem.chatInfo
// let cItem = aChatItem.chatItem
// chatModel.addChatItem(cInfo, cItem)
// NtfManager.shared.notifyMessageReceived(cInfo, cItem)
// case let .chatItemUpdated(aChatItem):
// let cInfo = aChatItem.chatInfo
// let cItem = aChatItem.chatItem
// if chatModel.upsertChatItem(cInfo, cItem) {
// NtfManager.shared.notifyMessageReceived(cInfo, cItem)
// }
// default:
// logger.debug("unsupported event: \(res.responseType)")
// }
}
}
class Mock: ChatController(0) {}
}

View file

@ -35,7 +35,7 @@ fun ChatView(chatModel: ChatModel, nav: NavController) {
mc = MsgContent.MCText(msg)
)
// hide "in progress"
// TODO add new item
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
}
}
})

View file

@ -237,6 +237,9 @@ func chatSendCmd(_ cmd: ChatCommand) throws -> ChatResponse {
logger.debug("chatSendCmd \(cmd.cmdType)")
let resp = chatResponse(chat_send_cmd(getChatCtrl(), &c)!)
logger.debug("chatSendCmd \(cmd.cmdType): \(resp.responseType)")
if case let .response(_, json) = resp {
logger.debug("chatSendCmd \(cmd.cmdType) response: \(json)")
}
DispatchQueue.main.async {
ChatModel.shared.terminalItems.append(.cmd(.now, cmd))
ChatModel.shared.terminalItems.append(.resp(.now, resp))