// // ContactList.swift // Monal // // Created by Jan on 15.12.22. // Copyright © 2022 monal-im.org. All rights reserved. // import OrderedCollections struct ContactPickerEntry: View { let contact : ObservableKVOWrapper let isPicked: Bool let isExistingMember: Bool var body:some View { ZStack(alignment: .topLeading) { HStack(alignment: .center) { if(isExistingMember) { Image(systemName: "checkmark.circle") .foregroundColor(.gray) } else if(isPicked) { Image(systemName: "checkmark.circle") .foregroundColor(.accentColor) } else { Image(systemName: "circle") } ContactEntry(contact: contact) } } } } struct ContactPicker: View { typealias completionType = (OrderedSet>)->Void let account: xmpp @Binding var returnedContacts: OrderedSet> @State var selectedContacts: OrderedSet> @State var searchText = "" @State var isEditingSearchInput = false let allowRemoval: Bool let completion: completionType? init(_ account: xmpp, initializeFrom contacts: OrderedSet>, allowRemoval: Bool = true, completion:completionType?) { self.account = account self.allowRemoval = allowRemoval self.completion = completion _selectedContacts = State(wrappedValue:OrderedSet()) //use a temporary storage because we don't have a binding to the outside world but use the completion handler var storage = contacts _returnedContacts = Binding( get: { storage }, set: { storage = $0 } ) buildPreselectedContacts(contacts) DDLogError("self.allowRemoval = \(String(describing:self.allowRemoval))") } init(_ account: xmpp, binding returnedContacts: Binding>>, allowRemoval: Bool = true) { self.account = account self.allowRemoval = allowRemoval self.completion = nil _selectedContacts = State(wrappedValue:OrderedSet()) _returnedContacts = returnedContacts buildPreselectedContacts(returnedContacts.wrappedValue) } private mutating func buildPreselectedContacts(_ source: OrderedSet>) { //build currently selected list of contacts var contactsTmp: OrderedSet> = OrderedSet() for contact in source { contactsTmp.append(contact) } _selectedContacts = State(wrappedValue:contactsTmp) } private var allContacts: OrderedSet> { //build list of all possible contacts on this account (excluding selfchat and other mucs) var contactsTmp: OrderedSet> = OrderedSet() for contact in DataLayer.sharedInstance().possibleGroupMembers(forAccount: account.accountID) { contactsTmp.append(ObservableKVOWrapper(contact)) } return contactsTmp } private var searchResults : OrderedSet> { if searchText.isEmpty { return self.allContacts } else { var filteredContacts: OrderedSet> = OrderedSet() for contact in self.allContacts { if (contact.contactDisplayName as String).lowercased().contains(searchText.lowercased()) || (contact.contactJid as String).contains(searchText.lowercased()) { filteredContacts.append(contact) } } return filteredContacts } } var body: some View { if(allContacts.isEmpty) { Text("No contacts to show :(") .navigationTitle("Contact Lists") } else { List(searchResults) { contact in let contactIsSelected = self.selectedContacts.contains(contact); let contactIsAlreadyMember = self.returnedContacts.contains(contact); ContactPickerEntry(contact: contact, isPicked: contactIsSelected, isExistingMember: !(!contactIsAlreadyMember || allowRemoval)) .onTapGesture { // only allow changes to members that are not already part of the group if(!contactIsAlreadyMember || allowRemoval) { if(contactIsSelected) { self.selectedContacts.remove(contact) } else { self.selectedContacts.append(contact) } } } } .searchable(text: $searchText, placement: .automatic, prompt: nil) .listStyle(.inset) .navigationBarTitle(Text("Contact Selection"), displayMode: .inline) .onDisappear { returnedContacts.removeAll() for contact in selectedContacts { returnedContacts.append(contact) } if let completion = completion { completion(returnedContacts) } } } } }