From 30164f648e90511daf829dae6bac6c141dfc9b7d Mon Sep 17 00:00:00 2001
From: Jan Stoyke <michael-jan.stoyke@tu-ilmenau.de>
Date: Sat, 18 Nov 2023 04:18:04 +0100
Subject: [PATCH] WIP: Create group skeleton

---
 Monal/Classes/ContactList.swift               |  83 +++++++++++++
 Monal/Classes/ContactsViewController.m        |  13 +-
 Monal/Classes/ContactsViewController.m.rej    |  14 +++
 Monal/Classes/CreateGroupMenu.swift           | 116 ++++++++++++++++++
 ...ownloadFiletransferSettingViewController.m |   2 +-
 Monal/Classes/MLMessageProcessor.m            |   2 +-
 Monal/Classes/SwiftuiHelpers.swift            |   4 +
 Monal/Monal.xcodeproj/project.pbxproj         |   8 ++
 8 files changed, 239 insertions(+), 3 deletions(-)
 create mode 100644 Monal/Classes/ContactList.swift
 create mode 100644 Monal/Classes/ContactsViewController.m.rej
 create mode 100644 Monal/Classes/CreateGroupMenu.swift

diff --git a/Monal/Classes/ContactList.swift b/Monal/Classes/ContactList.swift
new file mode 100644
index 0000000000..e0c1792a83
--- /dev/null
+++ b/Monal/Classes/ContactList.swift
@@ -0,0 +1,83 @@
+//
+//  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 {
+    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 ContactList: View {
+    @State var contacts : [MLContact]
+    @State var selectedContact : MLContact?
+    @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(contacts, id: \.self) { contact in
+                    if matchesSearch(contact: contact) {
+                        ContactEntry(contact: contact)
+                    }
+                }
+                .onDelete {
+                    print(contacts.remove(atOffsets: $0))
+                }
+                .onInsert(of: [""], perform: { _,_ in
+                })
+            }
+            .listStyle(.inset)
+            .navigationBarTitle("Contact List", displayMode: .inline)
+            .toolbar {
+                EditButton()
+            }
+        }
+    }
+}
+
+struct ContactList_Previews: PreviewProvider {
+    static var previews: some View {
+        ContactList(contacts: [
+            MLContact.makeDummyContact(0),
+            MLContact.makeDummyContact(1),
+            MLContact.makeDummyContact(2),
+            MLContact.makeDummyContact(3)]
+        )
+    }
+}
diff --git a/Monal/Classes/ContactsViewController.m b/Monal/Classes/ContactsViewController.m
index a76551ba1a..dd71765da4 100644
--- a/Monal/Classes/ContactsViewController.m
+++ b/Monal/Classes/ContactsViewController.m
@@ -47,7 +47,13 @@ -(void) openAddContacts:(id)sender
     [self presentViewController:addContactMenuView animated:YES completion:^{}];
 }
 
--(void) openContactRequests:(id)sender
+-(void) openCreateGroup:(id) sender
+{
+    UIViewController* createGroupView = [[SwiftuiInterface new] makeViewWithName:@"CreateGroup"];
+    [self presentViewController:createGroupView animated:YES completion:^{}];
+}
+
+-(void) openContactRequests:(id) sender
 {
     UIViewController* contactRequestsView = [[SwiftuiInterface new] makeViewWithName:@"ContactRequests"];
     [self presentViewController:contactRequestsView animated:YES completion:^{}];
@@ -100,6 +106,11 @@ -(void) viewDidLoad
     [addContact setAction:@selector(openAddContacts:)];
 
     self.navigationItem.rightBarButtonItems = [[NSArray alloc] initWithObjects:addContact, [UIBarButtonItem new], nil];
+    UIBarButtonItem* createGroup = [[UIBarButtonItem alloc] init];
+        createGroup.image = [UIImage systemImageNamed:@"person.3.fill"];
+        [createGroup setAction:@selector(openCreateGroup:)];
+
+    self.navigationItem.rightBarButtonItems = [[NSArray alloc] initWithObjects:addContact, [[UIBarButtonItem alloc] init], createGroup, nil];
     [self configureContactRequestsImage];
     
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContactUpdate) name:kMonalContactRemoved object:nil];
diff --git a/Monal/Classes/ContactsViewController.m.rej b/Monal/Classes/ContactsViewController.m.rej
new file mode 100644
index 0000000000..2bcb0d25b8
--- /dev/null
+++ b/Monal/Classes/ContactsViewController.m.rej
@@ -0,0 +1,14 @@
+diff a/Monal/Classes/ContactsViewController.m b/Monal/Classes/ContactsViewController.m	(rejected hunks)
+@@ -99,7 +105,11 @@ -(void) viewDidLoad
+     addContact.image = [UIImage systemImageNamed:@"person.fill.badge.plus"];
+     [addContact setAction:@selector(openAddContacts:)];
+ 
+-    self.navigationItem.rightBarButtonItems = [[NSArray alloc] initWithObjects:addContact, [[UIBarButtonItem alloc] init], nil];
++    UIBarButtonItem* createGroup = [[UIBarButtonItem alloc] init];
++    createGroup.image = [UIImage systemImageNamed:@"person.3.fill"];
++    [createGroup setAction:@selector(openCreateGroup:)];
++
++    self.navigationItem.rightBarButtonItems = [[NSArray alloc] initWithObjects:addContact, [[UIBarButtonItem alloc] init], createGroup, nil];
+     [self configureContactRequestsImage];
+     
+     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContactUpdate) name:kMonalContactRemoved object:nil];
diff --git a/Monal/Classes/CreateGroupMenu.swift b/Monal/Classes/CreateGroupMenu.swift
new file mode 100644
index 0000000000..f55f1f01a5
--- /dev/null
+++ b/Monal/Classes/CreateGroupMenu.swift
@@ -0,0 +1,116 @@
+//
+//  AddContactMenu.swift
+//  Monal
+//
+//  Created by Jan on 27.10.22.
+//  Copyright © 2022 monal-im.org. All rights reserved.
+//
+
+import MobileCoreServices
+import UniformTypeIdentifiers
+import SwiftUI
+import monalxmpp
+
+struct CreateGroupMenu: View {
+    var delegate: SheetDismisserProtocol
+
+    @State private var connectedAccounts: [xmpp]
+    @State private var selectedAccount: Int
+    @State private var groupName: String = ""
+
+    @State private var showAlert = false
+    // note: dismissLabel is not accessed but defined at the .alert() section
+    @State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close"))
+
+    @ObservedObject private var overlay = LoadingOverlayState()
+
+    @State private var showQRCodeScanner = false
+    @State private var success = false
+
+    private let dismissWithNewGroup: (MLContact) -> ()
+
+    init(delegate: SheetDismisserProtocol, dismissWithNewGroup: @escaping (MLContact) -> (), prefillJid: String = "", preauthToken:String? = nil) {
+        // FIXME
+        self.delegate = delegate
+        self.dismissWithNewGroup = dismissWithNewGroup
+        self.groupName = prefillJid
+        // self.preauthToken = preauthToken
+
+        let connectedAccounts = MLXMPPManager.sharedInstance().connectedXMPP as! [xmpp]
+        self.connectedAccounts = connectedAccounts
+        self.selectedAccount = connectedAccounts.first != nil ? 0 : -1;
+    }
+
+    // FIXME duplicate code from WelcomeLogIn.swift, maybe move to SwiftuiHelpers
+
+    private func errorAlert(title: Text, message: Text = Text("")) {
+        alertPrompt.title = title
+        alertPrompt.message = message
+        showAlert = true
+    }
+
+    private func successAlert(title: Text, message: Text) {
+        alertPrompt.title = title
+        alertPrompt.message = message
+        self.success = true // < dismiss entire view on close
+        showAlert = true
+    }
+
+    private var buttonColor: Color {
+        return Color(UIColor.systemBlue)
+    }
+
+    var body: some View {
+        Form {
+            if(connectedAccounts.isEmpty) {
+                Text("Please make sure at least one account has connected before trying to create new group.")
+                    .foregroundColor(.secondary)
+            }
+            else
+            {
+                Section() {
+                    if(connectedAccounts.count > 1) {
+                        Picker("Use account", selection: $selectedAccount) {
+                            ForEach(Array(self.connectedAccounts.enumerated()), id: \.element) { idx, account in
+                                Text(account.connectionProperties.identity.jid).tag(idx)
+                            }
+                        }
+                        .pickerStyle(.menu)
+                    }
+                    TextField("Group Name (optional)", text: $groupName)
+                        .autocorrectionDisabled()
+                        .autocapitalization(.none)
+                        .addClearButton(text:$groupName)
+
+                    NavigationLink(destination: LazyClosureView(ContactList(contacts: DataLayer.sharedInstance().contactList() as! [MLContact])), label: {
+                            Text("Group Members")
+                        })
+                }
+                Section {
+                    Button(action: {}, label: {
+                        Text("Create new group")
+                    })
+                }
+            }
+        }
+        .alert(isPresented: $showAlert) {
+            Alert(title: alertPrompt.title, message: alertPrompt.message, dismissButton:.default(Text("Close"), action: {
+                showAlert = false
+                if self.success == true {
+                    // TODO dismissWithNewGroup
+                }
+            }))
+        }
+        .addLoadingOverlay(overlay)
+        .navigationBarTitle("Create new group", displayMode: .inline)
+        .navigationViewStyle(.stack)
+    }
+}
+
+struct CreateGroupMenu_Previews: PreviewProvider {
+    static var delegate = SheetDismisserProtocol()
+    static var previews: some View {
+        CreateGroupMenu(delegate: delegate, dismissWithNewGroup: { c in
+        })
+    }
+}
diff --git a/Monal/Classes/MLAutoDownloadFiletransferSettingViewController.m b/Monal/Classes/MLAutoDownloadFiletransferSettingViewController.m
index 124b2a6ae5..45729bc7d7 100644
--- a/Monal/Classes/MLAutoDownloadFiletransferSettingViewController.m
+++ b/Monal/Classes/MLAutoDownloadFiletransferSettingViewController.m
@@ -106,7 +106,7 @@ -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NS
                         // byte -> mb
                         float mb = sliderValue / 1024 / 1024;
                         labelToUpdate.text = [NSString stringWithFormat:NSLocalizedString(@"Load over WiFi upto: %.fMB", @""), mb];
-
+                        
                         return mb;
                     } withUpdateFunc:^(UILabel* labelToUpdate, float sliderValue) {
                         float newValue = roundf(sliderValue);
diff --git a/Monal/Classes/MLMessageProcessor.m b/Monal/Classes/MLMessageProcessor.m
index 9ca0c08e02..5267e6f86c 100644
--- a/Monal/Classes/MLMessageProcessor.m
+++ b/Monal/Classes/MLMessageProcessor.m
@@ -774,7 +774,7 @@ +(MLMessage* _Nullable) processMessage:(XMPPMessage*) messageNode andOuterMessag
             }
         }
     }
-    
+
     return message;
 }
 
diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift
index 645e21dedf..bd9bc8390d 100644
--- a/Monal/Classes/SwiftuiHelpers.swift
+++ b/Monal/Classes/SwiftuiHelpers.swift
@@ -473,6 +473,10 @@ class SwiftuiInterface : NSObject {
                 host.rootView = AnyView(UIKitWorkaround(WelcomeLogIn(delegate:delegate)))
             case "ContactRequests":
                 host.rootView = AnyView(AddTopLevelNavigation(withDelegate: delegate, to: ContactRequestsMenu(delegate: delegate)))
+            case "CreateGroup":
+                host.rootView = AnyView(AddTopLevelNavigation(withDelegate: delegate, to: CreateGroupMenu(delegate: delegate, dismissWithNewGroup: { contact in
+                    // FIXME
+                })))
             case "ChatPlaceholder":
                 host.rootView = AnyView(ChatPlaceholder())
             default:
diff --git a/Monal/Monal.xcodeproj/project.pbxproj b/Monal/Monal.xcodeproj/project.pbxproj
index 0a46648240..0df43c2aa2 100644
--- a/Monal/Monal.xcodeproj/project.pbxproj
+++ b/Monal/Monal.xcodeproj/project.pbxproj
@@ -172,6 +172,8 @@
 		C1049189261301530054AC9E /* MonalXMPPUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1049188261301530054AC9E /* MonalXMPPUnitTests.swift */; };
 		C104918B261301530054AC9E /* monalxmpp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26CC579223A0867400ABB92A /* monalxmpp.framework */; };
 		C1049199261301710054AC9E /* MLCryptoTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1049198261301710054AC9E /* MLCryptoTest.swift */; };
+		C117F7E12B086390001F2BC6 /* CreateGroupMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D88BB76295BB6DC00FB30BA /* CreateGroupMenu.swift */; };
+		C117F7E22B0863B3001F2BC6 /* ContactList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D631822294BAB1D00026BE7 /* ContactList.swift */; };
 		C12436142434AB5D00B8F074 /* MLAttributedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = C12436132434AB5D00B8F074 /* MLAttributedLabel.m */; };
 		C13A0BCE26E78B7B00987E29 /* ContactDetailsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19C919A26E26AF000F8CC57 /* ContactDetailsHeader.swift */; };
 		C13EBB8E24DC685C008AADDA /* MLPrivacySettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C13EBB8D24DC685C008AADDA /* MLPrivacySettingsViewController.m */; };
@@ -500,10 +502,12 @@
 		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>"; };
 		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>"; };
 		3D85E586282AE523006F5B3A /* OmemoQrCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmemoQrCodeView.swift; sourceTree = "<group>"; };
+		3D88BB76295BB6DC00FB30BA /* CreateGroupMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupMenu.swift; sourceTree = "<group>"; };
 		3DC5035B2822F5220064C8A7 /* OmemoKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmemoKeys.swift; sourceTree = "<group>"; };
 		3EB7A7084FA9A8F68A3D251C /* Pods-MonalXMPPUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonalXMPPUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-MonalXMPPUnitTests/Pods-MonalXMPPUnitTests.debug.xcconfig"; sourceTree = "<group>"; };
 		4049F81F60EA5B7A57A4E9C6 /* Pods-NotificationService.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.beta.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.beta.xcconfig"; sourceTree = "<group>"; };
@@ -1049,7 +1053,9 @@
 				26A78ED623C2B59400C7CF40 /* MLPlaceholderViewController.h */,
 				26A78ED723C2B59400C7CF40 /* MLPlaceholderViewController.m */,
 				84FC375828981A5600634E3E /* PasswordMigration.swift */,
+				3D631822294BAB1D00026BE7 /* ContactList.swift */,
 				3D27D955290B0BB60014748B /* AddContactMenu.swift */,
+				3D88BB76295BB6DC00FB30BA /* CreateGroupMenu.swift */,
 				3D27D957290B0BC80014748B /* ContactRequestsMenu.swift */,
 				846DF27B2937FAA600AAB9C0 /* ChatPlaceholder.swift */,
 				841898AB2957DBAC00FEC77D /* RichAlert.swift */,
@@ -2013,6 +2019,7 @@
 				841B6F1A297B18720074F9B7 /* AccountPicker.swift in Sources */,
 				3D65B791272350F0005A30F4 /* SwiftuiHelpers.swift in Sources */,
 				C1A80DA424D9552400B99E01 /* MLChatViewHelper.m in Sources */,
+				C117F7E22B0863B3001F2BC6 /* ContactList.swift in Sources */,
 				1D60589B0D05DD56006BFB54 /* main.m in Sources */,
 				1D3623260D0F684500981E51 /* MonalAppDelegate.m in Sources */,
 				26158AF21FFA6E4500E53BDC /* MLWebViewController.m in Sources */,
@@ -2052,6 +2059,7 @@
 				C13A0BCE26E78B7B00987E29 /* ContactDetailsHeader.swift in Sources */,
 				3D85E587282AE523006F5B3A /* OmemoQrCodeView.swift in Sources */,
 				849A53E4287135B2007E941A /* MLVoIPProcessor.m in Sources */,
+				C117F7E12B086390001F2BC6 /* CreateGroupMenu.swift in Sources */,
 				26D59D9320714F32006F1DEE /* UIColor+Theme.m in Sources */,
 				3D7D352328626CB80042C5E5 /* LoadingOverlay.swift in Sources */,
 				26D7C05E23D6AFD800CA123C /* MLChatInputContainer.m in Sources */,