Skip to content

Commit

Permalink
WIP: contact picker
Browse files Browse the repository at this point in the history
  • Loading branch information
janst97 authored and FriedrichAltheide committed Nov 26, 2023
1 parent bdc3d1d commit 4035ea9
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 106 deletions.
83 changes: 0 additions & 83 deletions Monal/Classes/ContactList.swift

This file was deleted.

159 changes: 159 additions & 0 deletions Monal/Classes/ContactPicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// ContactList.swift
// Monal
//
// Created by Jan on 15.12.22.
// Copyright © 2022 monal-im.org. All rights reserved.
//

import SwiftUI
import monalxmpp

struct ContactEntry: View { // TODO move
let contact : MLContact

var body:some View {
ZStack(alignment: .topLeading) {
HStack(alignment: .center) {
Image(uiImage: contact.avatar)
.resizable()
.frame(width: 40, height: 40, alignment: .center)
VStack(alignment: .leading) {
Text(contact.contactDisplayName as String)
Text(contact.contactJid as String).font(.footnote).opacity(0.6)
}
}
}
}
}

struct ContactPickerEntry: View {
let contact : MLContact

@Binding var selectedByIndex : [Bool]
let idx : Int

var body:some View {
ZStack(alignment: .topLeading) {
HStack(alignment: .center) {
if(self.selectedByIndex[idx]) {
Image(systemName: "checkmark.circle")
.foregroundColor(.blue)
} else {
Image(systemName: "circle")
}
Image(uiImage: contact.avatar)
.resizable()
.frame(width: 40, height: 40, alignment: .center)
VStack(alignment: .leading) {
Text(contact.contactDisplayName as String)
Text(contact.contactJid as String).font(.footnote).opacity(0.6)
}
}
}
.onTapGesture(perform: {
self.selectedByIndex[idx] = !self.selectedByIndex[idx]
})
}
}

struct ContactPicker: View {
@Environment(\.presentationMode) private var presentationMode

private let selectedContactsCallback : ([MLContact]) -> Void

let contacts : [MLContact]
let selectedContacts : [MLContact] // already selected when going into the view
@State var selectedByIndex : [Bool] = []
@State var searchFieldInput = ""

func matchesSearch(contact : MLContact) -> Bool {
// TODO better lookup
if searchFieldInput.isEmpty == true {
return true
} else {
return contact.contactDisplayName.lowercased().contains(searchFieldInput.lowercased()) ||
contact.contactJid.contains(searchFieldInput.lowercased())
}
}

var body: some View {
if(contacts.isEmpty) {
Text("No contacts to show :(")
.navigationTitle("Contact Lists")
} else {
List {
Section {
TextField("Search contacts", text: $searchFieldInput)
}
ForEach(Array(contacts.enumerated()), id: \.element) { idx, contact in
if matchesSearch(contact: contact) {
ContactPickerEntry(contact: contact, selectedByIndex: $selectedByIndex, idx: idx)
}
}
}
.listStyle(.inset)
.navigationBarTitle("Contact Selection", displayMode: .inline)
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Back", action: {
var selectedContacts : [MLContact] = []
for (idx, selected) in self.selectedByIndex.enumerated() {
if(selected) {
selectedContacts.append(self.contacts[idx])
}
}
self.selectedContactsCallback(selectedContacts)
self.presentationMode.wrappedValue.dismiss()
})
}
}.onAppear(perform: {
self.selectedByIndex = [Bool].init(repeating: false, count: self.contacts.count)
for (idx, contact) in contacts.enumerated() {
var isSelected = false
for selected in self.selectedContacts {
if(contact.contactJid == selected.contactJid) {
isSelected = true
break;
}
}
if(isSelected) {
self.selectedByIndex[idx] = true
}
}
})
}
}

init(excludedContacts: [ObservableKVOWrapper<MLContact>], selectedContacts: [MLContact], selectedContactsCallback: @escaping ([MLContact]) -> Void) {
self.selectedContacts = selectedContacts
self.selectedContactsCallback = selectedContactsCallback
let allContacts = DataLayer.sharedInstance().contactList() as! [MLContact]
if excludedContacts.isEmpty {
self.contacts = allContacts
} else {
var withoutExcluded : [MLContact] = []
for contact in allContacts {
var isExcluded = false
for excluded in excludedContacts {
if(contact.contactJid == excluded.obj.contactJid) {
isExcluded = true
break;
}
}
if(!isExcluded) {
withoutExcluded.append(contact);
}
}
self.contacts = withoutExcluded
}
}
}

struct ContactList_Previews: PreviewProvider {
static var previews: some View {
ContactPicker(excludedContacts: [], selectedContacts: [], selectedContactsCallback: { contacts in
})
}
}
22 changes: 17 additions & 5 deletions Monal/Classes/CreateGroupMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct CreateGroupMenu: View {
@State private var showAlert = false
// note: dismissLabel is not accessed but defined at the .alert() section
@State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close"))
@State private var selectedContacts : [MLContact] = []

@ObservedObject private var overlay = LoadingOverlayState()

Expand Down Expand Up @@ -82,14 +83,25 @@ struct CreateGroupMenu: View {
.autocapitalization(.none)
.addClearButton(text:$groupName)

NavigationLink(destination: LazyClosureView(ContactList(contacts: DataLayer.sharedInstance().contactList() as! [MLContact])), label: {
NavigationLink(destination: LazyClosureView(ContactPicker(excludedContacts: [], selectedContacts: self.selectedContacts, selectedContactsCallback: { selectedContacts in
self.selectedContacts = selectedContacts
})), label: {
Text("Group Members")
})
}
Section {
Button(action: {}, label: {
Text("Create new group")
})
if(self.selectedContacts.count > 0) {
Section(header: Text("Selected Group Members")) {
ForEach(self.selectedContacts, id: \.self) { contact in
ContactEntry(contact: contact)
}
}
Section {
Button(action: {

}, label: {
Text("Create new group")
})
}
}
}
}
Expand Down
59 changes: 43 additions & 16 deletions Monal/Classes/MemberList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,29 @@ import SwiftUI
import monalxmpp

struct MemberList: View {
@Environment(\.editMode) private var editMode

private let memberList: [ObservableKVOWrapper<MLContact>]
private let groupName: String
private let account: xmpp?

@State private var openAccountSelection : Bool = false
@State private var contactsToAdd : [MLContact] = []

@State private var showAlert = false
@State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close"))
func setAndShowAlert(title: String, description: String) {
self.alertPrompt.title = Text(title)
self.alertPrompt.message = Text(description)
self.showAlert = true
}

var body: some View {
// This is the invisible NavigationLink hack again...
NavigationLink(destination:LazyClosureView(ContactPicker(excludedContacts: self.memberList, selectedContacts: [], selectedContactsCallback: { selectedContacts in
self.contactsToAdd = selectedContacts
setAndShowAlert(title: "Added contacts", description: "Selected " + String(selectedContacts.count) + " contact(s)")
})), isActive: $openAccountSelection){}.hidden().disabled(true) // navigation happens as soon as our button sets navigateToQRCodeView to true...
List {
Section(header: Text(self.groupName)) {
ForEach(self.memberList, id: \.self.obj) {
Expand All @@ -27,30 +45,39 @@ struct MemberList: View {
.resizable()
.frame(width: 40, height: 40, alignment: .center)
Text(contact.contactDisplayName as String)
if(editMode?.wrappedValue.isEditing == true) {
Spacer()
Button(action: {}, label: {
Image(systemName: "slider.horizontal.3")
})
}
}
/*Button(action: {
}, label: {
Image(systemName: "xmark.circle.fill").foregroundColor(.red)
})
.buttonStyle(.borderless)
.offset(x: -7, y: -7)*/
}
})
}
}
.onDelete(perform: { memberIdx in
// TODO maybe alert before deletion
if(memberIdx.count == 1) {
self.setAndShowAlert(title: "Member deleted", description: self.memberList[memberIdx.first!].contactJid)
}
})
}.alert(isPresented: $showAlert, content: {
Alert(title: alertPrompt.title, message: alertPrompt.message, dismissButton: .default(alertPrompt.dismissLabel))
})
}
.toolbar {
if(editMode?.wrappedValue.isEditing == true) {
Button(action: {
openAccountSelection = true
}, label: {
Image(systemName: "plus")
.foregroundColor(.blue)
})
}
EditButton()
}
.navigationBarTitle("Group Members", displayMode: .inline)
/*.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
HStack{
Button(action: {
}, label: {
Image(systemName: "person.fill.badge.plus")
})
}
}
}*/
}

init(mucContact: ObservableKVOWrapper<MLContact>?) {
Expand Down
4 changes: 2 additions & 2 deletions Monal/Monal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@
3D27D955290B0BB60014748B /* AddContactMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactMenu.swift; sourceTree = "<group>"; };
3D27D957290B0BC80014748B /* ContactRequestsMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestsMenu.swift; sourceTree = "<group>"; };
3D5A91412842B4AE008CE57E /* MemberList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberList.swift; sourceTree = "<group>"; };
3D631822294BAB1D00026BE7 /* ContactList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactList.swift; sourceTree = "<group>"; };
3D631822294BAB1D00026BE7 /* ContactPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPicker.swift; sourceTree = "<group>"; };
3D65B78C27234B74005A30F4 /* ContactDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetails.swift; sourceTree = "<group>"; };
3D65B790272350F0005A30F4 /* SwiftuiHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftuiHelpers.swift; sourceTree = "<group>"; };
3D7D352228626CB80042C5E5 /* LoadingOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingOverlay.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1053,7 +1053,7 @@
26A78ED623C2B59400C7CF40 /* MLPlaceholderViewController.h */,
26A78ED723C2B59400C7CF40 /* MLPlaceholderViewController.m */,
84FC375828981A5600634E3E /* PasswordMigration.swift */,
3D631822294BAB1D00026BE7 /* ContactList.swift */,
3D631822294BAB1D00026BE7 /* ContactPicker.swift */,
3D27D955290B0BB60014748B /* AddContactMenu.swift */,
3D88BB76295BB6DC00FB30BA /* CreateGroupMenu.swift */,
3D27D957290B0BC80014748B /* ContactRequestsMenu.swift */,
Expand Down

0 comments on commit 4035ea9

Please sign in to comment.