ios: search in chat (#938)

* ios: search in chat

* update libraries and search API

* layout
This commit is contained in:
Evgeny Poberezkin
2022-08-17 11:43:18 +01:00
committed by GitHub
parent ae6fae5ced
commit fd27839442
4 changed files with 115 additions and 54 deletions
+6 -6
View File
@@ -185,22 +185,22 @@ func apiGetChats() throws -> [ChatData] {
throw r
}
func apiGetChat(type: ChatType, id: Int64) throws -> Chat {
let r = chatSendCmdSync(.apiGetChat(type: type, id: id, pagination: .last(count: 50)))
func apiGetChat(type: ChatType, id: Int64, search: String = "") throws -> Chat {
let r = chatSendCmdSync(.apiGetChat(type: type, id: id, pagination: .last(count: 50), search: search))
if case let .apiChat(chat) = r { return Chat.init(chat) }
throw r
}
func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination) async throws -> [ChatItem] {
let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: pagination))
func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, search: String = "") async throws -> [ChatItem] {
let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: pagination, search: search))
if case let .apiChat(chat) = r { return chat.chatItems }
throw r
}
func loadChat(chat: Chat) {
func loadChat(chat: Chat, search: String = "") {
do {
let cInfo = chat.chatInfo
let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId)
let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
let m = ChatModel.shared
m.updateChatInfo(chat.chatInfo)
m.reversedChatItems = chat.chatItems.reversed()
+86 -18
View File
@@ -28,13 +28,19 @@ struct ChatView: View {
@State private var firstPage = false
@State private var itemsInView: Set<String> = []
@State private var scrollProxy: ScrollViewProxy?
@State private var searchMode = false
@State private var searchText: String = ""
@FocusState private var searchFocussed
var body: some View {
let cInfo = chat.chatInfo
return VStack {
return VStack(spacing: 0) {
if searchMode {
searchToolbar()
Divider()
}
ZStack(alignment: .trailing) {
chatItemsList(cInfo)
chatItemsList()
if let proxy = scrollProxy {
floatingButtons(proxy)
}
@@ -47,11 +53,12 @@ struct ChatView: View {
composeState: $composeState,
keyboardVisible: $keyboardVisible
)
.disabled(!chat.chatInfo.sendMsgEnabled)
.disabled(!cInfo.sendMsgEnabled)
}
.padding(.top, 1)
.navigationTitle(cInfo.chatViewName)
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
@@ -62,10 +69,7 @@ struct ChatView: View {
}
}
} label: {
HStack(spacing: 4) {
Image(systemName: "chevron.backward")
Text("Chats", comment: "back button to return to chats list")
}
Image(systemName: "chevron.backward")
}
}
ToolbarItem(placement: .principal) {
@@ -108,25 +112,75 @@ struct ChatView: View {
case let .direct(contact):
HStack {
callButton(contact, .audio, imageName: "phone")
callButton(contact, .video, imageName: "video")
Menu {
Button {
CallController.shared.startCall(contact, .video)
} label: {
Label("Video call", systemImage: "video")
}
searchButton()
} label: {
Image(systemName: "ellipsis")
}
}
case let .group(groupInfo):
if groupInfo.canAddMembers {
addMembersButton()
.sheet(isPresented: $showAddMembersSheet) {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
}
HStack {
if groupInfo.canAddMembers {
addMembersButton()
.sheet(isPresented: $showAddMembersSheet) {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
}
}
Menu {
searchButton()
} label: {
Image(systemName: "ellipsis")
}
}
default:
EmptyView()
}
}
}
.navigationBarBackButtonHidden(true)
}
private func chatItemsList(_ cInfo: ChatInfo) -> some View {
GeometryReader { g in
private func searchToolbar() -> some View {
HStack {
HStack {
Image(systemName: "magnifyingglass")
TextField("Search", text: $searchText)
.focused($searchFocussed)
.foregroundColor(.primary)
.frame(maxWidth: .infinity)
Button {
searchText = ""
} label: {
Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
}
}
.padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
.foregroundColor(.secondary)
.background(Color(.secondarySystemBackground))
.cornerRadius(10.0)
Button ("Cancel") {
searchText = ""
searchMode = false
searchFocussed = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
chatModel.reversedChatItems = []
loadChat(chat: chat)
}
}
}
.padding(.horizontal)
.padding(.vertical, 8)
}
private func chatItemsList() -> some View {
let cInfo = chat.chatInfo
return GeometryReader { g in
ScrollViewReader { proxy in
ScrollView {
let maxWidth =
@@ -161,6 +215,9 @@ struct ChatView: View {
scrollProxy = proxy
}
.onTapGesture { hideKeyboard() }
.onChange(of: searchText) { _ in
loadChat(chat: chat, search: searchText)
}
}
}
.scaleEffect(x: 1, y: -1, anchor: .center)
@@ -225,6 +282,16 @@ struct ChatView: View {
}
}
private func searchButton() -> some View {
Button {
searchMode = true
searchFocussed = true
searchText = ""
} label: {
Label("Search", systemImage: "magnifyingglass")
}
}
private func addMembersButton() -> some View {
Button {
if case let .group(gInfo) = chat.chatInfo {
@@ -250,7 +317,8 @@ struct ChatView: View {
let items = try await apiGetChatItems(
type: cInfo.chatType,
id: cInfo.apiId,
pagination: .before(chatItemId: firstItem.id, count: 50)
pagination: .before(chatItemId: firstItem.id, count: 50),
search: searchText
)
await MainActor.run {
if items.count == 0 {
+20 -28
View File
@@ -12,8 +12,12 @@
3C8C548928133C84000A3EC7 /* PasteToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */; };
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; };
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; };
5C00164028A1B87B0094D739 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 5C00163F28A1B87B0094D739 /* Introspect */; };
5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00164328A26FBC0094D739 /* ContextMenu.swift */; };
5C00164C28ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00164728ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a */; };
5C00164D28ACEA380094D739 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00164828ACEA380094D739 /* libgmp.a */; };
5C00164E28ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00164928ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a */; };
5C00164F28ACEA380094D739 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00164A28ACEA380094D739 /* libffi.a */; };
5C00165028ACEA380094D739 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00164B28ACEA380094D739 /* libgmpxx.a */; };
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA72837DBB3004A9677 /* CICallItemView.swift */; };
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
@@ -54,11 +58,6 @@
5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; };
5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; };
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */; };
5C9C2D9F28929B6900CC63B1 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9A28929B6900CC63B1 /* libffi.a */; };
5C9C2DA028929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9B28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a */; };
5C9C2DA128929B6900CC63B1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9C28929B6900CC63B1 /* libgmp.a */; };
5C9C2DA228929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9D28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a */; };
5C9C2DA328929B6900CC63B1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9C2D9E28929B6900CC63B1 /* libgmpxx.a */; };
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */; };
5C9C2DA7289957AE00CC63B1 /* AdvancedNetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */; };
5C9C2DA92899DA6F00CC63B1 /* NetworkAndServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */; };
@@ -197,6 +196,11 @@
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = "<group>"; };
3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = "<group>"; };
5C00164328A26FBC0094D739 /* ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenu.swift; sourceTree = "<group>"; };
5C00164728ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a"; sourceTree = "<group>"; };
5C00164828ACEA380094D739 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C00164928ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a"; sourceTree = "<group>"; };
5C00164A28ACEA380094D739 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C00164B28ACEA380094D739 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C029EA72837DBB3004A9677 /* CICallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CICallItemView.swift; sourceTree = "<group>"; };
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
@@ -239,11 +243,6 @@
5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoView.swift; sourceTree = "<group>"; };
5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoImage.swift; sourceTree = "<group>"; };
5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetNotificationsMode.swift; sourceTree = "<group>"; };
5C9C2D9A28929B6900CC63B1 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C9C2D9B28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a"; sourceTree = "<group>"; };
5C9C2D9C28929B6900CC63B1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C9C2D9D28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a"; sourceTree = "<group>"; };
5C9C2D9E28929B6900CC63B1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupProfileView.swift; sourceTree = "<group>"; };
5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedNetworkSettings.swift; sourceTree = "<group>"; };
5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkAndServers.swift; sourceTree = "<group>"; };
@@ -326,7 +325,6 @@
buildActionMask = 2147483647;
files = (
5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */,
5C00164028A1B87B0094D739 /* Introspect in Frameworks */,
646BB38C283BEEB9001CE359 /* LocalAuthentication.framework in Frameworks */,
5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */,
);
@@ -351,13 +349,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C9C2DA028929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a in Frameworks */,
5C9C2DA128929B6900CC63B1 /* libgmp.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C9C2DA228929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a in Frameworks */,
5C9C2D9F28929B6900CC63B1 /* libffi.a in Frameworks */,
5C00164F28ACEA380094D739 /* libffi.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C9C2DA328929B6900CC63B1 /* libgmpxx.a in Frameworks */,
5C00164E28ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a in Frameworks */,
5C00164D28ACEA380094D739 /* libgmp.a in Frameworks */,
5C00164C28ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a in Frameworks */,
5C00165028ACEA380094D739 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -412,11 +410,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C9C2D9A28929B6900CC63B1 /* libffi.a */,
5C9C2D9C28929B6900CC63B1 /* libgmp.a */,
5C9C2D9E28929B6900CC63B1 /* libgmpxx.a */,
5C9C2D9D28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw-ghc8.10.7.a */,
5C9C2D9B28929B6900CC63B1 /* libHSsimplex-chat-3.1.0-FNUbBjLYHjnDjt6ldpTolw.a */,
5C00164A28ACEA380094D739 /* libffi.a */,
5C00164828ACEA380094D739 /* libgmp.a */,
5C00164B28ACEA380094D739 /* libgmpxx.a */,
5C00164728ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz-ghc8.10.7.a */,
5C00164928ACEA380094D739 /* libHSsimplex-chat-3.1.0-iRSBBAuo4W6e6BG2FodFz.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -690,7 +688,6 @@
name = "SimpleX (iOS)";
packageProductDependencies = (
5C8F01CC27A6F0D8007D2C8D /* CodeScanner */,
5C00163F28A1B87B0094D739 /* Introspect */,
);
productName = "SimpleX (iOS)";
productReference = 5CA059CA279559F40002BEB4 /* SimpleX.app */;
@@ -1502,11 +1499,6 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
5C00163F28A1B87B0094D739 /* Introspect */ = {
isa = XCSwiftPackageProductDependency;
package = 5C00163E28A1B87B0094D739 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */;
productName = Introspect;
};
5C8F01CC27A6F0D8007D2C8D /* CodeScanner */ = {
isa = XCSwiftPackageProductDependency;
package = 5C8F01CB27A6F0D8007D2C8D /* XCRemoteSwiftPackageReference "CodeScanner" */;
+3 -2
View File
@@ -24,7 +24,7 @@ public enum ChatCommand {
case apiImportArchive(config: ArchiveConfig)
case apiDeleteStorage
case apiGetChats
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination)
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String)
case apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent)
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent)
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
@@ -85,7 +85,8 @@ public enum ChatCommand {
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
case .apiDeleteStorage: return "/_db delete"
case .apiGetChats: return "/_get chats pcc=on"
case let .apiGetChat(type, id, pagination): return "/_get chat \(ref(type, id)) \(pagination.cmdString)"
case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" +
(search == "" ? "" : " search=\(search)")
case let .apiSendMessage(type, id, file, quotedItemId, mc):
let msg = encodeJSON(ComposedMessage(filePath: file, quotedItemId: quotedItemId, msgContent: mc))
return "/_send \(ref(type, id)) json \(msg)"