initial api integration and types

This commit is contained in:
Diogo
2024-11-05 10:28:18 +00:00
parent d8d990a437
commit e1f24e7588
7 changed files with 84 additions and 14 deletions
+21 -5
View File
@@ -320,15 +320,24 @@ private func apiChatsResponse(_ r: ChatResponse) throws -> [ChatData] {
let loadItemsPerPage = 50
func apiGetChat(type: ChatType, id: Int64, search: String = "") async throws -> Chat {
func apiGetChat(type: ChatType, id: Int64, search: String = "") async throws -> (Chat, ChatGap?) {
let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: .last(count: loadItemsPerPage), search: search))
if case let .apiChat(_, chat) = r { return Chat.init(chat) }
if case let .apiChat(_, chat, gap) = r { return (Chat.init(chat), gap) }
throw r
}
func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, search: String = "") async throws -> [ChatItem] {
func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, search: String = "") async throws -> ([ChatItem], ChatGap?) {
let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: pagination, search: search))
if case let .apiChat(_, chat) = r { return chat.chatItems }
if case let .apiChat(_, chat, gap) = r { return (chat.chatItems, gap) }
if case .chatCmdError(_, _) = r {
if case .chatError(_, let chatError) = r {
if case .errorStore(let storeError) = chatError {
if case .chatItemNotFound(let itemId) = storeError {
itemNotFoundAlert()
}
}
}
}
throw r
}
@@ -341,7 +350,7 @@ func loadChat(chat: Chat, search: String = "", clearItems: Bool = true) async {
if clearItems {
await MainActor.run { im.reversedChatItems = [] }
}
let chat = try await apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
let (chat, _) = try await apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
await MainActor.run {
im.reversedChatItems = chat.chatItems.reversed()
m.updateChatInfo(chat.chatInfo)
@@ -418,6 +427,13 @@ func apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]
return nil
}
func itemNotFoundAlert() {
AlertManager.shared.showAlertMsg(
title: "Message no longer available",
message: "The quoted message you are trying to view has been deleted."
)
}
private func sendMessageErrorAlert(_ r: ChatResponse) {
logger.error("send message error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
@@ -48,10 +48,18 @@ struct FramedItemView: View {
if let qi = chatItem.quotedItem {
ciQuoteView(qi)
.onTapGesture {
if let ci = ItemsModel.shared.reversedChatItems.first(where: { $0.id == qi.itemId }) {
withAnimation {
scrollModel.scrollToItem(id: ci.id)
if let itemId = qi.itemId {
if !scrollToItem(itemId) {
Task {
if await loadItemsAround(chat.chatInfo, itemId) != nil {
await MainActor.run {
let _ = scrollToItem(itemId)
}
}
}
}
} else {
itemNotFoundAlert()
}
}
} else if let itemForwarded = chatItem.meta.itemForwarded {
@@ -323,6 +331,42 @@ struct FramedItemView: View {
return videoWidth
}
}
// Scroll to an item, if success returns true otherwise false
private func scrollToItem(_ itemId: Int64) -> Bool {
if let ci = ItemsModel.shared.reversedChatItems.first(where: { $0.id == itemId }) {
withAnimation {
scrollModel.scrollToItem(id: ci.id)
}
return true
} else {
return false
}
}
private func loadItemsAround(_ cInfo: ChatInfo, _ chatItemId: Int64) async -> [ChatItem]? {
do {
var reversedPage = Array<ChatItem>()
let pagination: ChatPagination = .around(chatItemId: chatItemId, count: loadItemsPerPage * 2)
let (chatItems, _) = try await apiGetChatItems(
type: cInfo.chatType,
id: cInfo.apiId,
pagination: pagination,
search: ""
)
reversedPage.append(contentsOf: chatItems.reversed())
await MainActor.run {
ItemsModel.shared.reversedChatItems.append(contentsOf: reversedPage)
}
return reversedPage
} catch let error {
logger.error("apiGetChat error: \(responseError(error))")
return nil
}
}
}
@ViewBuilder func toggleSecrets<V: View>(_ ft: [FormattedText]?, _ showSecrets: Binding<Bool>, _ v: V) -> some View {
+1 -1
View File
@@ -854,7 +854,7 @@ struct ChatView: View {
} else {
.last(count: loadItemsPerPage)
}
let chatItems = try await apiGetChatItems(
let (chatItems, _) = try await apiGetChatItems(
type: cInfo.chatType,
id: cInfo.apiId,
pagination: pagination,
@@ -340,7 +340,7 @@ struct GroupMemberInfoView: View {
InfoViewButton(image: "message.fill", title: "message", width: width) {
Task {
do {
let chat = try await apiGetChat(type: .direct, id: contactId)
let (chat, _) = try await apiGetChat(type: .direct, id: contactId)
chatModel.addChat(chat)
ItemsModel.shared.loadOpenChat(chat.id) {
dismissAllSheets(animated: true)
+2 -1
View File
@@ -214,7 +214,8 @@ public func chatResponse(_ s: String) -> ChatResponse {
let user: UserRef = try? decodeObject(jApiChat["user"] as Any),
let jChat = jApiChat["chat"] as? NSDictionary,
let chat = try? parseChatData(jChat) {
return .apiChat(user: user, chat: chat)
let gap: ChatGap? = try? decodeObject(jApiChat["gap"] as Any)
return .apiChat(user: user, chat: chat, gap: gap)
}
} else if type == "chatCmdError" {
if let jError = jResp["chatCmdError"] as? NSDictionary {
+7 -3
View File
@@ -546,7 +546,7 @@ public enum ChatResponse: Decodable, Error {
case chatStopped
case chatSuspended
case apiChats(user: UserRef, chats: [ChatData])
case apiChat(user: UserRef, chat: ChatData)
case apiChat(user: UserRef, chat: ChatData, gap: ChatGap?)
case chatItemInfo(user: UserRef, chatItem: AChatItem, chatItemInfo: ChatItemInfo)
case userProtoServers(user: UserRef, servers: UserProtoServers)
case serverTestResult(user: UserRef, testServer: String, testFailure: ProtocolTestFailure?)
@@ -888,7 +888,7 @@ public enum ChatResponse: Decodable, Error {
case .chatStopped: return noDetails
case .chatSuspended: return noDetails
case let .apiChats(u, chats): return withUser(u, String(describing: chats))
case let .apiChat(u, chat): return withUser(u, String(describing: chat))
case let .apiChat(u, chat, gap): return withUser(u, "gap: \(String(describing: gap)) \(String(describing: chat))")
case let .chatItemInfo(u, chatItem, chatItemInfo): return withUser(u, "chatItem: \(String(describing: chatItem))\nchatItemInfo: \(String(describing: chatItemInfo))")
case let .userProtoServers(u, servers): return withUser(u, "servers: \(String(describing: servers))")
case let .serverTestResult(u, server, testFailure): return withUser(u, "server: \(server)\nresult: \(String(describing: testFailure))")
@@ -1133,12 +1133,16 @@ public enum ChatPagination {
case last(count: Int)
case after(chatItemId: Int64, count: Int)
case before(chatItemId: Int64, count: Int)
case around(chatItemId: Int64, count: Int)
case initial(count: Int)
var cmdString: String {
switch self {
case let .last(count): return "count=\(count)"
case let .after(chatItemId, count): return "after=\(chatItemId) count=\(count)"
case let .before(chatItemId, count): return "before=\(chatItemId) count=\(count)"
case let .around(chatItemId, count): return "around=\(chatItemId) count=\(count)"
case let .initial(count): return "initial=\(count)"
}
}
}
+5
View File
@@ -1529,6 +1529,11 @@ public struct ChatStats: Decodable, Hashable {
public var unreadChat: Bool = false
}
public struct ChatGap: Decodable, Hashable {
public var index: Int?
public var size: Int
}
public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var contactId: Int64
var localDisplayName: ContactName