diff --git a/.github/workflows/beta.build-push.yml b/.github/workflows/beta.build-push.yml index da2feffead..cfe75aba88 100644 --- a/.github/workflows/beta.build-push.yml +++ b/.github/workflows/beta.build-push.yml @@ -77,15 +77,15 @@ jobs: echo "name=Monal Beta $(git log -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1 (Build '$buildNumber', PR \2)/g')" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1/g' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes_ios<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*MACOS_ONLY.*$' | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*MACOS_ONLY.*$' | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes_macos<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*IOS_ONLY.*$' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*IOS_ONLY.*$' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" cat "$OUTPUT_FILE" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/stable.build-push.yml b/.github/workflows/stable.build-push.yml index c28ba1143c..e372161755 100644 --- a/.github/workflows/stable.build-push.yml +++ b/.github/workflows/stable.build-push.yml @@ -74,15 +74,15 @@ jobs: echo "name=Monal $(git log -n 1 --merges --pretty=format:%s | sed -E 's/^[\t\n ]*([^\n\t ]+)[\t\n ]+\(([^\n\t ]+)\)[\t\n ]*$/\1 (Build '$buildNumber', PR \2)/g')" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1/g' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes_ios<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*MACOS_ONLY.*$' | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*MACOS_ONLY.*$' | sed -E 's/^[\t\n ]*IOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" echo "notes_macos<<__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" - echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*IOS_ONLY.*$' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1' | tee /dev/stderr >> "$OUTPUT_FILE" + echo "$(git log -n 1 --merges --pretty=format:%b)" | grep -v '^[\t\n ]*IOS_ONLY.*$' | sed -E 's/^[\t\n ]*MACOS_ONLY[\t\n ]*(.*)$/\1/g' | tee /dev/stderr >> "$OUTPUT_FILE" echo "__EOF__" | tee /dev/stderr >> "$OUTPUT_FILE" cat "$OUTPUT_FILE" >> "$GITHUB_OUTPUT" diff --git a/Monal/Classes/ActiveChatsViewController.h b/Monal/Classes/ActiveChatsViewController.h index aa46b7f5ec..7efd519214 100644 --- a/Monal/Classes/ActiveChatsViewController.h +++ b/Monal/Classes/ActiveChatsViewController.h @@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN -(void) showDetails; -(void) showRegisterWithUsername:(NSString*) username onHost:(NSString*) host withToken:(NSString* _Nullable) token usingCompletion:(monal_id_block_t _Nullable) callback; -(void) showAddContactWithJid:(NSString*) jid preauthToken:(NSString* _Nullable) preauthToken prefillAccount:(xmpp* _Nullable) account andOmemoFingerprints:(NSDictionary* _Nullable) fingerprints; +-(void) sheetDismissed; @end diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index 30f676cb0b..839a4af5b1 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -69,17 +69,18 @@ +(void) initialize -(void) configureComposeButton { - UIImage* composeImage = [[UIImage systemImageNamed:@"person.2.fill"] imageWithTintColor:UIColor.monalGreen]; + UIImage* image = [[UIImage systemImageNamed:@"person.2.fill"] imageWithTintColor:UIColor.monalGreen]; + UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showContacts:)]; + self.composeButton.customView = [HelperTools + buttonWithNotificationBadgeForImage:image + hasNotification:[[DataLayer sharedInstance] allContactRequests].count > 0 + withTapHandler:tapRecognizer]; + [self.composeButton setIsAccessibilityElement:YES]; if([[DataLayer sharedInstance] allContactRequests].count > 0) - { - self.composeButton.image = [HelperTools imageWithNotificationBadgeForImage:composeImage]; - } + [self.composeButton setAccessibilityLabel:NSLocalizedString(@"Open contact list (contact requests pending)", @"")]; else - { - self.composeButton.image = composeImage; - } - [self.composeButton setAccessibilityLabel:@"Open contacts list"]; - [self.composeButton setAccessibilityHint:NSLocalizedString(@"Open contact list", @"")]; + [self.composeButton setAccessibilityLabel:NSLocalizedString(@"Open contact list", @"")]; + [self.composeButton setAccessibilityTraits:UIAccessibilityTraitButton]; } -(void) viewDidLoad diff --git a/Monal/Classes/AddContactMenu.swift b/Monal/Classes/AddContactMenu.swift index dbe2d5d898..2c7960cc11 100644 --- a/Monal/Classes/AddContactMenu.swift +++ b/Monal/Classes/AddContactMenu.swift @@ -172,6 +172,10 @@ struct AddContactMenu: View { } else { + if DataLayer.sharedInstance().allContactRequests().count > 0 { + ContactRequestsMenu() + } + Section(header:Text("Contact and Group/Channel Jids are usually in the format: name@domain.tld")) { if connectedAccounts.count > 1 { Picker("Use account", selection: $selectedAccount) { @@ -190,18 +194,16 @@ struct AddContactMenu: View { .addClearButton(isEditing: isEditingJid, text:$toAdd) .disabled(scannedFingerprints != nil) .foregroundColor(scannedFingerprints != nil ? .secondary : .primary) - .onChange(of: toAdd) { _ in - toAdd = toAdd.replacingOccurrences(of: " ", with: "") - } - } - if scannedFingerprints != nil && scannedFingerprints!.count > 0 { - Section(header: Text("A contact was scanned through the QR code scanner")) { - Toggle(isOn: $importScannedFingerprints) { - Text("Import and trust OMEMO fingerprints from QR code") + .onChange(of: toAdd) { _ in toAdd = toAdd.replacingOccurrences(of: " ", with: "") } + + if scannedFingerprints != nil && scannedFingerprints!.count > 0 { + Section(header: Text("A contact was scanned through the QR code scanner")) { + Toggle(isOn: $importScannedFingerprints) { + Text("Import and trust OMEMO fingerprints from QR code") + } } } - } - Section { + if scannedFingerprints != nil { Button(action: { toAdd = "" @@ -212,26 +214,43 @@ struct AddContactMenu: View { .foregroundColor(.red) }) } - Button(action: { - showAlert = toAddEmptyAlert || toAddInvalidAlert + + HStack { + Spacer() + + Button(action: { + showAlert = toAddEmptyAlert || toAddInvalidAlert - if !showAlert { - let jidComponents = HelperTools.splitJid(toAdd) - if jidComponents["host"] == nil || jidComponents["host"]!.isEmpty { - errorAlert(title: Text("Error"), message: Text("Something went wrong while parsing your input...")) - showAlert = true - return + if !showAlert { + let jidComponents = HelperTools.splitJid(toAdd) + if jidComponents["host"] == nil || jidComponents["host"]!.isEmpty { + errorAlert(title: Text("Error"), message: Text("Something went wrong while parsing your input...")) + showAlert = true + return + } + // use the canonized jid from now on (lowercased, resource removed etc.) + addJid(jid: jidComponents["user"]!) } - // use the canonized jid from now on (lowercased, resource removed etc.) - addJid(jid: jidComponents["user"]!) + }) { + scannedFingerprints == nil ? Text("Add") : Text("Add scanned contact") } - }, label: { - scannedFingerprints == nil ? Text("Add Group/Channel or Contact") : Text("Add scanned Group/Channel or Contact") - }) - .disabled(toAddEmpty || toAddInvalid) + //.fontWeight(.bold) + .padding() + .background(toAddEmpty || toAddInvalid ? Color.gray : Color.blue) + .foregroundColor(.white) + .cornerRadius(10) + .disabled(toAddEmpty || toAddInvalid) + } + } + + if DataLayer.sharedInstance().allContactRequests().count == 0 { + Section { + ContactRequestsMenu() + } } } } + .padding() .alert(isPresented: $showAlert) { Alert(title: alertPrompt.title, message: alertPrompt.message, dismissButton:.default(Text("Close"), action: { showAlert = false diff --git a/Monal/Classes/ContactRequestsMenu.swift b/Monal/Classes/ContactRequestsMenu.swift index 023caeda9f..bbb8caf0df 100644 --- a/Monal/Classes/ContactRequestsMenu.swift +++ b/Monal/Classes/ContactRequestsMenu.swift @@ -66,26 +66,22 @@ struct ContactRequestsMenu: View { @State private var pendingRequests: [MLContact] var body: some View { - Form { - List { - Section(header: Text("Allowing someone to add you as a contact lets them see your profile picture and when you are online.")) { - if(pendingRequests.isEmpty) { - Text("No pending requests") - .foregroundColor(.secondary) - } - ForEach(pendingRequests.indices, id: \.self) { idx in - ContactRequestsMenuEntry( - contact: pendingRequests[idx], - doDelete: { - self.pendingRequests.remove(at: idx) - } - ) - } + List { + Section(header: Text("Allowing someone to add you as a contact lets them see your profile picture and when you are online.")) { + if(pendingRequests.isEmpty) { + Text("No pending constact requests") + .foregroundColor(.secondary) + } + ForEach(pendingRequests.indices, id: \.self) { idx in + ContactRequestsMenuEntry( + contact: pendingRequests[idx], + doDelete: { + self.pendingRequests.remove(at: idx) + } + ) } } } - .navigationBarTitle(Text("Contact Requests"), displayMode: .inline) - .navigationViewStyle(.stack) .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRefresh")).receive(on: RunLoop.main)) { notification in self.pendingRequests = DataLayer.sharedInstance().allContactRequests() as! [MLContact] } diff --git a/Monal/Classes/ContactsViewController.m b/Monal/Classes/ContactsViewController.m index 3adace979f..ed27f5d6a0 100644 --- a/Monal/Classes/ContactsViewController.m +++ b/Monal/Classes/ContactsViewController.m @@ -53,24 +53,20 @@ -(void) openCreateGroup:(id) sender [self presentViewController:createGroupView animated:YES completion:^{}]; } --(void) openContactRequests:(id) sender +-(void) configureAddContactImage { - UIViewController* contactRequestsView = [[SwiftuiInterface new] makeViewWithName:@"ContactRequests"]; - [self presentViewController:contactRequestsView animated:YES completion:^{}]; -} - --(void) configureContactRequestsImage -{ - UIImage* requestsImage = [[UIImage systemImageNamed:@"person.crop.circle.fill.badge.questionmark"] imageWithTintColor:UIColor.monalGreen]; - UITapGestureRecognizer* requestsTapRecoginzer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openContactRequests:)]; - self.navigationItem.rightBarButtonItems[1].customView = [HelperTools - buttonWithNotificationBadgeForImage:requestsImage + UIImage* image = [[UIImage systemImageNamed:@"person.fill.badge.plus"] imageWithTintColor:UIColor.monalGreen]; + UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openAddContacts:)]; + self.navigationItem.rightBarButtonItems[0].customView = [HelperTools + buttonWithNotificationBadgeForImage:image hasNotification:[[DataLayer sharedInstance] allContactRequests].count > 0 - withTapHandler:requestsTapRecoginzer]; - [self.navigationItem.rightBarButtonItems[1] setIsAccessibilityElement:YES]; - [self.navigationItem.rightBarButtonItems[1] setAccessibilityLabel:NSLocalizedString(@"Pending contact requests", @"")]; - [self.navigationItem.rightBarButtonItems[1] setAccessibilityTraits:UIAccessibilityTraitButton]; - + withTapHandler:tapRecognizer]; + [self.navigationItem.rightBarButtonItems[0] setIsAccessibilityElement:YES]; + if([[DataLayer sharedInstance] allContactRequests].count > 0) + [self.navigationItem.rightBarButtonItems[0] setAccessibilityLabel:NSLocalizedString(@"Add contact (contact requests pending)", @"")]; + else + [self.navigationItem.rightBarButtonItems[0] setAccessibilityLabel:NSLocalizedString(@"Add contact", @"")]; + [self.navigationItem.rightBarButtonItems[0] setAccessibilityTraits:UIAccessibilityTraitButton]; } #pragma mark view life cycle @@ -105,20 +101,15 @@ -(void) viewDidLoad self.tableView.emptyDataSetSource = self; self.tableView.emptyDataSetDelegate = self; - UIBarButtonItem* addContact = [UIBarButtonItem new]; - addContact.image = [UIImage systemImageNamed:@"person.fill.badge.plus"]; - addContact.accessibilityLabel = @"Add contact"; - [addContact setAction:@selector(openAddContacts:)]; - [addContact setTarget:self]; - UIBarButtonItem* createGroup = [[UIBarButtonItem alloc] init]; createGroup.image = [UIImage systemImageNamed:@"person.3.fill"]; createGroup.accessibilityLabel = @"Create contact group"; [createGroup setAction:@selector(openCreateGroup:)]; [createGroup setTarget:self]; - self.navigationItem.rightBarButtonItems = @[addContact, [[UIBarButtonItem alloc] init], createGroup]; + + self.navigationItem.rightBarButtonItems = @[[[UIBarButtonItem alloc] init], createGroup]; - [self configureContactRequestsImage]; + [self configureAddContactImage]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContactUpdate) name:kMonalContactRemoved object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContactUpdate) name:kMonalContactRefresh object:nil]; @@ -194,7 +185,7 @@ -(BOOL) canBecomeFirstResponder -(void) reloadTable { - [self configureContactRequestsImage]; + [self configureAddContactImage]; if(self.contactsTable.hasUncommittedUpdates) return; [self.contactsTable reloadData]; diff --git a/Monal/Classes/CreateGroupMenu.swift b/Monal/Classes/CreateGroupMenu.swift index 8cb54c522f..f3692a7740 100644 --- a/Monal/Classes/CreateGroupMenu.swift +++ b/Monal/Classes/CreateGroupMenu.swift @@ -45,12 +45,14 @@ struct CreateGroupMenu: View { else { Section() { - Picker(selection: $selectedAccount, label: Text("Use account")) { - ForEach(Array(self.connectedAccounts.enumerated()), id: \.element) { idx, account in - Text(account.connectionProperties.identity.jid).tag(account as xmpp?) + if connectedAccounts.count > 1 { + Picker(selection: $selectedAccount, label: Text("Use account")) { + ForEach(Array(self.connectedAccounts.enumerated()), id: \.element) { idx, account in + Text(account.connectionProperties.identity.jid).tag(account as xmpp?) + } } + .pickerStyle(.menu) } - .pickerStyle(.menu) TextField(NSLocalizedString("Group Name (optional)", comment: "placeholder when creating new group"), text: $groupName, onEditingChanged: { isEditingGroupName = $0 }) .autocorrectionDisabled() diff --git a/Monal/Classes/HelperTools.h b/Monal/Classes/HelperTools.h index cabe9eca55..ea4fd932b2 100644 --- a/Monal/Classes/HelperTools.h +++ b/Monal/Classes/HelperTools.h @@ -147,6 +147,7 @@ void swizzle(Class c, SEL orig, SEL new); +(NSSet*) getOwnFeatureSet; +(NSString*) getEntityCapsHashForIdentities:(NSArray*) identities andFeatures:(NSSet*) features andForms:(NSArray*) forms; +(NSString* _Nullable) formatLastInteraction:(NSDate*) lastInteraction; ++(NSString*) stringFromTimeInterval:(NSUInteger) interval; +(NSDate*) parseDateTimeString:(NSString*) datetime; +(NSString*) generateDateTimeString:(NSDate*) datetime; +(NSString*) encodeRandomResource; diff --git a/Monal/Classes/HelperTools.m b/Monal/Classes/HelperTools.m index 9d690661fb..300bb79fd3 100644 --- a/Monal/Classes/HelperTools.m +++ b/Monal/Classes/HelperTools.m @@ -2219,6 +2219,15 @@ +(NSString* _Nullable) formatLastInteraction:(NSDate*) lastInteraction } } ++(NSString*) stringFromTimeInterval:(NSUInteger) interval +{ + NSUInteger hours = interval / 3600; + NSUInteger minutes = (interval % 3600) / 60; + NSUInteger seconds = interval % 60; + + return [NSString stringWithFormat:@"%luh %lumin and %lusec", hours, minutes, seconds]; +} + +(NSDate*) parseDateTimeString:(NSString*) datetime { static NSDateFormatter* rfc3339DateFormatter; diff --git a/Monal/Classes/MLSettingsTableViewController.m b/Monal/Classes/MLSettingsTableViewController.m index a73297329e..ae684d6da6 100644 --- a/Monal/Classes/MLSettingsTableViewController.m +++ b/Monal/Classes/MLSettingsTableViewController.m @@ -13,6 +13,8 @@ #import "DataLayer.h" #import "MLXMPPManager.h" #import "XMPPEdit.h" +#import "MonalAppDelegate.h" +#import "ActiveChatsViewController.h" #import @import SafariServices; @@ -106,6 +108,12 @@ -(void) viewWillAppear:(BOOL)animated self.selected = nil; } +-(void) viewWillDisappear:(BOOL) animated +{ + [super viewWillDisappear:animated]; + [((MonalAppDelegate*)UIApplication.sharedApplication.delegate).activeChats sheetDismissed]; +} + #pragma mark - key commands -(BOOL) canBecomeFirstResponder diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index df3f4e82c4..8827721083 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -711,8 +711,6 @@ class SwiftuiInterface : NSObject { host = UIHostingController(rootView:AnyView(AddTopLevelNavigation(withDelegate:delegate, to:WelcomeLogIn(delegate:delegate)))) case "LogIn": host = UIHostingController(rootView:AnyView(UIKitWorkaround(WelcomeLogIn(delegate:delegate)))) - case "ContactRequests": - host = UIHostingController(rootView:AnyView(AddTopLevelNavigation(withDelegate: delegate, to: ContactRequestsMenu()))) case "CreateGroup": host = UIHostingController(rootView:AnyView(AddTopLevelNavigation(withDelegate: delegate, to: CreateGroupMenu(delegate: delegate)))) case "ChatPlaceholder":