From bd664ddd33bb9c186c340f1ed25723c8fb947bc7 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 6 May 2024 15:40:27 +0400 Subject: [PATCH] wip --- .../Views/Chat/Contacts/ContactsView.swift | 149 ++++++++++++++++++ .../ios/Shared/Views/ChatList/ChatsView.swift | 12 -- apps/ios/Shared/Views/Home/HomeView.swift | 73 ++++++--- apps/ios/SimpleX.xcodeproj/project.pbxproj | 12 ++ 4 files changed, 208 insertions(+), 38 deletions(-) create mode 100644 apps/ios/Shared/Views/Chat/Contacts/ContactsView.swift diff --git a/apps/ios/Shared/Views/Chat/Contacts/ContactsView.swift b/apps/ios/Shared/Views/Chat/Contacts/ContactsView.swift new file mode 100644 index 0000000000..cb76407885 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/Contacts/ContactsView.swift @@ -0,0 +1,149 @@ +// +// ContactsView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 06.05.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ContactsView: View { + @EnvironmentObject var chatModel: ChatModel + + @State private var searchMode = false + @FocusState private var searchFocussed + @State private var searchText = "" + + @AppStorage(DEFAULT_ONE_HAND_UI) private var oneHandUI = true + + var body: some View { + if #available(iOS 16.0, *) { + viewBody.scrollDismissesKeyboard(.immediately) + } else { + viewBody + } + } + + private var viewBody: some View { + VStack { + contactList + } + .listStyle(.plain) + } + + @ViewBuilder private var contactList: some View { + let cs = filteredContacts() + ZStack { + VStack { + List { + if !chatModel.chats.isEmpty { + ContactsSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText + ) + .listRowSeparator(.hidden) + .frame(maxWidth: .infinity) + } + ForEach(cs, id: \.viewId) { chat in + ChatListNavLink(chat: chat) + .padding(.trailing, -16) + .disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id)) + } + .offset(x: -8) + } + } + if cs.isEmpty && !chatModel.chats.isEmpty { + Text("No filtered contacts") + .foregroundColor(.secondary) + } + } + } + + private func filteredContacts() -> [Chat] { + let s = searchString() + return chatModel.chats.filter { chat in + let cInfo = chat.chatInfo + switch cInfo { + case let .direct(contact): + return s == "" + ? true + : (viewNameContains(cInfo, s) || + contact.profile.displayName.localizedLowercase.contains(s) || + contact.fullName.localizedLowercase.contains(s)) + default: + return false + } + } + + func searchString() -> String { + searchText.trimmingCharacters(in: .whitespaces).localizedLowercase + } + + func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool { + cInfo.chatViewName.localizedLowercase.contains(s) + } + } +} + +struct ContactsSearchBar: View { + @EnvironmentObject var m: ChatModel + @Binding var searchMode: Bool + @FocusState.Binding var searchFocussed: Bool + @Binding var searchText: String + + var body: some View { + VStack(spacing: 12) { + HStack(spacing: 12) { + HStack(spacing: 4) { + Image(systemName: "magnifyingglass") + TextField("Search", text: $searchText) + .focused($searchFocussed) + .frame(maxWidth: .infinity) + if !searchText.isEmpty { + Image(systemName: "xmark.circle.fill") + .onTapGesture { + searchText = "" + } + } + } + .padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7)) + .foregroundColor(.secondary) + .background(Color(.tertiarySystemFill)) + .cornerRadius(10.0) + + if searchFocussed { + Text("Cancel") + .foregroundColor(.accentColor) + .onTapGesture { + searchText = "" + searchFocussed = false + } + } + } + } + .onChange(of: searchFocussed) { sf in + withAnimation { searchMode = sf } + } + } +} + +struct ContactsView_Previews: PreviewProvider { + static var previews: some View { + let chatModel = ChatModel() + chatModel.chats = [ + Chat( + chatInfo: ChatInfo.sampleData.direct, + chatItems: [] + ) + ] + return Group { + ContactsView() + .environmentObject(chatModel) + ContactsView() + .environmentObject(ChatModel()) + } + } +} diff --git a/apps/ios/Shared/Views/ChatList/ChatsView.swift b/apps/ios/Shared/Views/ChatList/ChatsView.swift index 2314f1c1fd..5ef4328db2 100644 --- a/apps/ios/Shared/Views/ChatList/ChatsView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatsView.swift @@ -254,18 +254,6 @@ struct ChatsSearchBar: View { } } -// TODO remove -func chatsStoppedIcon() -> some View { - Button { - AlertManager.shared.showAlertMsg( - title: "Chat is stopped", - message: "You can start chat via app Settings / Database or by restarting the app" - ) - } label: { - Image(systemName: "exclamationmark.octagon.fill").foregroundColor(.red) - } -} - struct ChatsView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() diff --git a/apps/ios/Shared/Views/Home/HomeView.swift b/apps/ios/Shared/Views/Home/HomeView.swift index 5e8558de34..5c5bff54bf 100644 --- a/apps/ios/Shared/Views/Home/HomeView.swift +++ b/apps/ios/Shared/Views/Home/HomeView.swift @@ -182,32 +182,56 @@ struct HomeView: View { .foregroundColor(homeTab == .chats ? .accentColor : .secondary) } - private func newChatButton() -> some View { - Menu { - Button { - newChatMenuOption = .newGroup + @ViewBuilder private func newChatButton() -> some View { + if case .some(false) = chatModel.chatRunning { + chatsStoppedButton() + } else { + Menu { + Button { + newChatMenuOption = .newGroup + } label: { + Text("Create group") + } + Button { + newChatMenuOption = .scanPaste + } label: { + Text("Scan / Paste link") + } + Button { + newChatMenuOption = .newContact + } label: { + Text("Add contact") + } } label: { - Text("Create group") + iconLabel("square.and.pencil", "New chat") } - Button { - newChatMenuOption = .scanPaste - } label: { - Text("Scan / Paste link") + .foregroundColor(.secondary) + .sheet(item: $newChatMenuOption) { opt in + switch opt { + case .newContact: NewChatView(selection: .invite) + case .scanPaste: NewChatView(selection: .connect, showQRCodeScanner: true) + case .newGroup: AddGroupView() + } } - Button { - newChatMenuOption = .newContact - } label: { - Text("Add contact") - } - } label: { - iconLabel("square.and.pencil", "New chat") } - .foregroundColor(.secondary) - .sheet(item: $newChatMenuOption) { opt in - switch opt { - case .newContact: NewChatView(selection: .invite) - case .scanPaste: NewChatView(selection: .connect, showQRCodeScanner: true) - case .newGroup: AddGroupView() + } + + func chatsStoppedButton() -> some View { + Button { + AlertManager.shared.showAlertMsg( + title: "Chat is stopped", + message: "You can start chat via app Settings / Database or by restarting the app" + ) + } label: { + VStack(spacing: 4) { + Image(systemName: "exclamationmark.octagon.fill") + .resizable() + .scaledToFit() + .foregroundColor(.red) + .frame(width: 24, height: 24) + Text("Stopped") + .font(.caption) + .foregroundColor(.secondary) } } } @@ -224,10 +248,7 @@ struct HomeView: View { } private func contactsView() -> some View { - // TODO - VStack { - Text("Contacts") - } + ContactsView() } @ViewBuilder private func chatsView() -> some View { diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 1d3c76ee59..b482b97a43 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -174,6 +174,7 @@ 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; }; 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; }; 648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; }; + 6485F7C62BE8DB6500FAE413 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6485F7C52BE8DB6500FAE413 /* ContactsView.swift */; }; 648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; }; 649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; }; 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; }; @@ -471,6 +472,7 @@ 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = ""; }; 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = ""; }; 648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = ""; }; + 6485F7C52BE8DB6500FAE413 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = ""; }; 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = ""; }; 6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = ""; }; @@ -586,6 +588,7 @@ 5C5F4AC227A5E9AF00B51EF1 /* Chat */ = { isa = PBXGroup; children = ( + 6485F7C42BE8DB4F00FAE413 /* Contacts */, 6440CA01288AEC770062C672 /* Group */, 5CE4407427ADB657007B033A /* ChatItem */, 5CEACCE527DE977C000BD591 /* ComposeMessage */, @@ -927,6 +930,14 @@ path = Group; sourceTree = ""; }; + 6485F7C42BE8DB4F00FAE413 /* Contacts */ = { + isa = PBXGroup; + children = ( + 6485F7C52BE8DB6500FAE413 /* ContactsView.swift */, + ); + path = Contacts; + sourceTree = ""; + }; 8C7D94982B8894D300B7B9E1 /* Migration */ = { isa = PBXGroup; children = ( @@ -1182,6 +1193,7 @@ 5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */, 5CB634AF29E4BB7D0066AD6B /* SetAppPasscodeView.swift in Sources */, 5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */, + 6485F7C62BE8DB6500FAE413 /* ContactsView.swift in Sources */, 5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */, 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */, 648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,