Эх сурвалжийг харах

Refactor NCMenu

Makes it easy for any ViewController to display a menu, reduce code duplication

Signed-off-by: Henrik Storch <henrik.storch@nextcloud.com>
Henrik Storch 3 жил өмнө
parent
commit
1c42318869

+ 6 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -17,6 +17,7 @@
 		370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
 		370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
 		371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */; };
 		371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */; };
 		3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */; };
 		3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */; };
+		8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */; };
 		D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */; };
 		D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */; };
 		D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
 		D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; };
 		F700222C1EC479840080073F /* Custom.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F700222B1EC479840080073F /* Custom.xcassets */; };
 		F700222C1EC479840080073F /* Custom.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F700222B1EC479840080073F /* Custom.xcassets */; };
@@ -423,6 +424,8 @@
 		371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMenu.swift; sourceTree = "<group>"; };
 		371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMenu.swift; sourceTree = "<group>"; };
 		371B5A3223D0BD5500FAFAE9 /* FloatingPanel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FloatingPanel.framework; path = Carthage/Build/iOS/FloatingPanel.framework; sourceTree = "<group>"; };
 		371B5A3223D0BD5500FAFAE9 /* FloatingPanel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FloatingPanel.framework; path = Carthage/Build/iOS/FloatingPanel.framework; sourceTree = "<group>"; };
 		3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = "<group>"; };
 		3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = "<group>"; };
+		8491B1C8273BB3D6001C8C5B /* ios-communication-library */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "ios-communication-library"; path = "../ios-communication-library"; sourceTree = "<group>"; };
+		8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Menu.swift"; sourceTree = "<group>"; };
 		D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = "<group>"; };
 		D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = "<group>"; };
 		F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = "<group>"; };
 		F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = "<group>"; };
 		F700510022DF63AC003A3356 /* NCShare.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCShare.storyboard; sourceTree = "<group>"; };
 		F700510022DF63AC003A3356 /* NCShare.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCShare.storyboard; sourceTree = "<group>"; };
@@ -860,6 +863,7 @@
 				3704EB2923D5A58400455C5B /* NCMenu.storyboard */,
 				3704EB2923D5A58400455C5B /* NCMenu.storyboard */,
 				371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */,
 				371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */,
 				3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */,
 				3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */,
+				8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */,
 				F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */,
 				F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */,
 				F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */,
 				F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */,
 				F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */,
 				F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */,
@@ -1477,6 +1481,7 @@
 		F7F67B9F1A24D27800EE80DA = {
 		F7F67B9F1A24D27800EE80DA = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				8491B1C8273BB3D6001C8C5B /* ios-communication-library */,
 				F7F67BAA1A24D27800EE80DA /* iOSClient */,
 				F7F67BAA1A24D27800EE80DA /* iOSClient */,
 				F7F67BAB1A24D27800EE80DA /* Supporting Files */,
 				F7F67BAB1A24D27800EE80DA /* Supporting Files */,
 				F771E3D120E2392D00AFB62D /* File Provider Extension */,
 				F771E3D120E2392D00AFB62D /* File Provider Extension */,
@@ -2184,6 +2189,7 @@
 				F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
 				F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
 				F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */,
 				F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */,
 				F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */,
 				F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */,
+				8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */,
 				F702F2F725EE5CED008F8E80 /* NCLogin.swift in Sources */,
 				F702F2F725EE5CED008F8E80 /* NCLogin.swift in Sources */,
 				F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
 				F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */,
 				F7DBC37C23325E02001A85BA /* NCAppConfigView.swift in Sources */,
 				F7DBC37C23325E02001A85BA /* NCAppConfigView.swift in Sources */,

+ 2 - 10
iOSClient/Activity/NCActivity.swift

@@ -446,7 +446,6 @@ extension NCActivity: NCShareCommentsCellDelegate {
     
     
     func toggleMenu(with tableComments: tableComments?) {
     func toggleMenu(with tableComments: tableComments?) {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions = [NCMenuAction]()
         var actions = [NCMenuAction]()
 
 
         actions.append(
         actions.append(
@@ -504,15 +503,8 @@ extension NCActivity: NCShareCommentsCellDelegate {
                 icon: UIImage(named: "cancel")!.image(color: NCBrandColor.shared.gray, size: 50),
                 icon: UIImage(named: "cancel")!.image(color: NCBrandColor.shared.gray, size: 50),
                 action: nil)
                 action: nil)
         )
         )
-        
-        menuViewController.actions = actions
-
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-        self.present(menuPanelController, animated: true, completion: nil)
+
+        presentMenu(with: actions)
     }
     }
 }
 }
 
 

+ 2 - 11
iOSClient/Menu/AppDelegate+Menu.swift

@@ -31,7 +31,6 @@ extension AppDelegate {
     
     
     func toggleMenu(viewController: UIViewController) {
     func toggleMenu(viewController: UIViewController) {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions: [NCMenuAction] = []
         var actions: [NCMenuAction] = []
         
         
         let appDelegate = UIApplication.shared.delegate as! AppDelegate
         let appDelegate = UIApplication.shared.delegate as! AppDelegate
@@ -295,15 +294,7 @@ extension AppDelegate {
                 )
                 )
             }
             }
         }
         }
-
-        menuViewController.actions = actions
-
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = viewController
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        viewController.present(menuPanelController, animated: true, completion: nil)
+        
+        viewController.presentMenu(with: actions)
     }
     }
 }
 }

+ 3 - 96
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -27,98 +27,6 @@ import UIKit
 import FloatingPanel
 import FloatingPanel
 import NCCommunication
 import NCCommunication
 import Queuer
 import Queuer
-import SVGKit
-
-extension UIViewController {
-    func handleProfileAction(_ action: NCHovercard.Action, for userId: String) {
-        
-        switch action.appId {
-        case "email":
-            guard let url = action.hyperlinkUrl,
-                  url.scheme == "mailto",
-                  let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
-                NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
-                return
-            }
-
-            sendEmail(to: components.path)
-        case "spreed":
-            let appDelegate = UIApplication.shared.delegate as! AppDelegate
-            if let talkUrl = URL(string: "nextcloudtalk://open-conversation?server=\(appDelegate.urlBase)&user=\(userId)&withUser=\(appDelegate.userId)"),
-               UIApplication.shared.canOpenURL(talkUrl) {
-                UIApplication.shared.open(talkUrl, options: [.universalLinksOnly: true])
-            } else if let url = action.hyperlinkUrl {
-                UIApplication.shared.open(url, options: [:])
-            }
-        default:
-            guard let url = action.hyperlinkUrl, UIApplication.shared.canOpenURL(url) else {
-                NCContentPresenter.shared.showGenericError(description: "_open_url_error")
-                return
-            }
-            UIApplication.shared.open(url, options: [:])
-        }
-    }
-    
-    func showProfileMenu(userId: String) {
-
-        NCCommunication.shared.getHovercard(for: userId) { (card, errCode, err) in
-            guard let card = card else {
-                return
-            }
-            let appDelegate = UIApplication.shared.delegate as! AppDelegate
-            let personHeader = NCMenuAction(
-                title: card.displayName,
-                icon: NCUtility.shared.loadUserImage(for: userId, displayName: card.displayName, urlBase: appDelegate.urlBase),
-                action: nil)
-            
-            let actions = card.actions.map { action -> NCMenuAction in
-                var image = UIImage()
-                if let url = URL(string: action.icon), let svg = SVGKImage(contentsOf: url) {
-                    image = svg.uiImage
-                }
-                return NCMenuAction(
-                    title: action.title,
-                    icon: image,
-                    action: { _ in self.handleProfileAction(action, for: userId) })
-            }
-            
-            let allActions = [personHeader] + actions
-            self.presentMenu(with: allActions)
-        }
-    }
-    
-    func sendEmail(to email: String) {
-        guard MFMailComposeViewController.canSendMail() else {
-            NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
-            return
-        }
-
-        let mail = MFMailComposeViewController()
-        mail.mailComposeDelegate = self
-        mail.setToRecipients([email])
-
-        present(mail, animated: true)
-    }
-    
-    fileprivate func presentMenu(with actions: [NCMenuAction]) {
-        let menuViewController = NCMenu.makeNCMenu(with: actions)
-        
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-        
-        present(menuPanelController, animated: true, completion: nil)
-    }
-}
-
-extension UIViewController: MFMailComposeViewControllerDelegate {
-    public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
-        controller.dismiss(animated: true)
-    }
-}
-
 
 
 extension NCCollectionViewCommon {
 extension NCCollectionViewCommon {
     
     
@@ -543,10 +451,9 @@ extension NCCollectionViewCommon {
     }
     }
     
     
     func toggleMenuSelect() {
     func toggleMenuSelect() {
-        
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
+
         var actions = [NCMenuAction]()
         var actions = [NCMenuAction]()
-       
+
         //
         //
         // SELECT ALL
         // SELECT ALL
         //
         //
@@ -672,7 +579,7 @@ extension NCCollectionViewCommon {
                 }
                 }
             )
             )
         )
         )
-        
+
         presentMenu(with: actions)
         presentMenu(with: actions)
     }
     }
 }
 }

+ 1 - 10
iOSClient/Menu/NCLoginWeb+Menu.swift

@@ -28,7 +28,6 @@ extension NCLoginWeb {
 
 
     func toggleMenu() {
     func toggleMenu() {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions = [NCMenuAction]()
         var actions = [NCMenuAction]()
         
         
         let accounts = NCManageDatabase.shared.getAllAccount()
         let accounts = NCManageDatabase.shared.getAllAccount()
@@ -82,16 +81,8 @@ extension NCLoginWeb {
                 }
                 }
             )
             )
         )
         )
-       
-        menuViewController.actions = actions
 
 
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        self.present(menuPanelController, animated: true, completion: nil)
+        presentMenu(with: actions)
     }
     }
 }
 }
 
 

+ 2 - 10
iOSClient/Menu/NCMedia+Menu.swift

@@ -28,8 +28,7 @@ import NCCommunication
 extension NCMedia {
 extension NCMedia {
 
 
     func toggleMenu() {
     func toggleMenu() {
-        
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
+
         var actions: [NCMenuAction] = []
         var actions: [NCMenuAction] = []
 
 
         if !isEditMode {
         if !isEditMode {
@@ -261,14 +260,7 @@ extension NCMedia {
             )
             )
         }
         }
 
 
-        menuViewController.actions = actions
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        self.present(menuPanelController, animated: true, completion: nil)
+        presentMenu(with: actions)
     }
     }
 }
 }
 
 

+ 1 - 1
iOSClient/Menu/NCMenu.swift

@@ -31,7 +31,7 @@ class NCMenu: UITableViewController {
     var actions = [NCMenuAction]()
     var actions = [NCMenuAction]()
 
 
     static func makeNCMenu(with actions: [NCMenuAction]) -> NCMenu {
     static func makeNCMenu(with actions: [NCMenuAction]) -> NCMenu {
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
+        let menuViewController = UIStoryboard(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         menuViewController.actions = actions
         menuViewController.actions = actions
         return menuViewController
         return menuViewController
     }
     }

+ 1 - 10
iOSClient/Menu/NCSortMenu.swift

@@ -41,7 +41,6 @@ class NCSortMenu: NSObject {
         self.hideDirectoryOnTop = hideDirectoryOnTop
         self.hideDirectoryOnTop = hideDirectoryOnTop
         
         
         var layoutForView = NCUtility.shared.getLayoutForView(key: key, serverUrl: serverUrl)
         var layoutForView = NCUtility.shared.getLayoutForView(key: key, serverUrl: serverUrl)
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions = [NCMenuAction]()
         var actions = [NCMenuAction]()
         var title = ""
         var title = ""
         var icon = UIImage()
         var icon = UIImage()
@@ -127,15 +126,7 @@ class NCSortMenu: NSObject {
             )
             )
         }
         }
         
         
-        menuViewController.actions = actions
-
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = viewController
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        viewController.present(menuPanelController, animated: true, completion: nil)
+        viewController.presentMenu(with: actions)
     }
     }
     
     
     func actionMenu(layoutForView: NCGlobal.layoutForViewType) {
     func actionMenu(layoutForView: NCGlobal.layoutForViewType) {

+ 3 - 22
iOSClient/Menu/NCTrash+Menu.swift

@@ -29,7 +29,6 @@ extension NCTrash {
 
 
     func toggleMenuMoreHeader() {
     func toggleMenuMoreHeader() {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions: [NCMenuAction] = []
         var actions: [NCMenuAction] = []
                 
                 
         if isEditMode {
         if isEditMode {
@@ -71,20 +70,11 @@ extension NCTrash {
             )
             )
         }
         }
         
         
-        menuViewController.actions = actions
-        
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        self.present(menuPanelController, animated: true, completion: nil)
+        presentMenu(with: actions)
     }
     }
     
     
     func toggleMenuMoreList(with objectId: String, image: UIImage?) {
     func toggleMenuMoreList(with objectId: String, image: UIImage?) {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions: [NCMenuAction] = []
         var actions: [NCMenuAction] = []
 
 
         guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
         guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
@@ -132,8 +122,7 @@ extension NCTrash {
     }
     }
     
     
     func toggleMenuMoreGrid(with objectId: String, namedButtonMore: String, image: UIImage?) {
     func toggleMenuMoreGrid(with objectId: String, namedButtonMore: String, image: UIImage?) {
-        
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
+
         var actions: [NCMenuAction] = []
         var actions: [NCMenuAction] = []
 
 
         guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
         guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: objectId, account: appDelegate.account) else {
@@ -179,15 +168,7 @@ extension NCTrash {
             )
             )
         )
         )
 
 
-        menuViewController.actions = actions
-
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = self
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
-
-        self.present(menuPanelController, animated: true, completion: nil)
+        presentMenu(with: actions)
     }
     }
 }
 }
 
 

+ 1 - 10
iOSClient/Menu/NCViewer+Menu.swift

@@ -29,7 +29,6 @@ extension NCViewer {
 
 
     func toggleMenu(viewController: UIViewController, metadata: tableMetadata, webView: Bool, imageIcon: UIImage?) {
     func toggleMenu(viewController: UIViewController, metadata: tableMetadata, webView: Bool, imageIcon: UIImage?) {
         
         
-        let menuViewController = UIStoryboard.init(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu
         var actions = [NCMenuAction]()
         var actions = [NCMenuAction]()
         
         
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
@@ -372,15 +371,7 @@ extension NCViewer {
                 )
                 )
             )
             )
         }
         }
-        
-        menuViewController.actions = actions
-        
-        let menuPanelController = NCMenuPanelController()
-        menuPanelController.parentPresenter = viewController
-        menuPanelController.delegate = menuViewController
-        menuPanelController.set(contentViewController: menuViewController)
-        menuPanelController.track(scrollView: menuViewController.tableView)
 
 
-        viewController.present(menuPanelController, animated: true, completion: nil)
+        viewController.presentMenu(with: actions)
     }
     }
 }
 }

+ 100 - 0
iOSClient/Menu/UIViewController+Menu.swift

@@ -0,0 +1,100 @@
+//
+//  NCHovercard+Menu.swift
+//  Nextcloud
+//
+//  Created by admin on 10.11.21.
+//  Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import SVGKit
+
+extension UIViewController {
+    func handleProfileAction(_ action: NCHovercard.Action, for userId: String) {
+        
+        switch action.appId {
+        case "email":
+            guard let url = action.hyperlinkUrl,
+                  url.scheme == "mailto",
+                  let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
+                NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
+                return
+            }
+
+            sendEmail(to: components.path)
+        case "spreed":
+            let appDelegate = UIApplication.shared.delegate as! AppDelegate
+            if let talkUrl = URL(string: "nextcloudtalk://open-conversation?server=\(appDelegate.urlBase)&user=\(userId)&withUser=\(appDelegate.userId)"),
+               UIApplication.shared.canOpenURL(talkUrl) {
+                UIApplication.shared.open(talkUrl, options: [.universalLinksOnly: true])
+            } else if let url = action.hyperlinkUrl {
+                UIApplication.shared.open(url, options: [:])
+            }
+        default:
+            guard let url = action.hyperlinkUrl, UIApplication.shared.canOpenURL(url) else {
+                NCContentPresenter.shared.showGenericError(description: "_open_url_error")
+                return
+            }
+            UIApplication.shared.open(url, options: [:])
+        }
+    }
+    
+    func showProfileMenu(userId: String) {
+
+        NCCommunication.shared.getHovercard(for: userId) { (card, errCode, err) in
+            guard let card = card else {
+                return
+            }
+            let appDelegate = UIApplication.shared.delegate as! AppDelegate
+            let personHeader = NCMenuAction(
+                title: card.displayName,
+                icon: NCUtility.shared.loadUserImage(for: userId, displayName: card.displayName, urlBase: appDelegate.urlBase),
+                action: nil)
+            
+            let actions = card.actions.map { action -> NCMenuAction in
+                var image = UIImage()
+                if let url = URL(string: action.icon), let svg = SVGKImage(contentsOf: url) {
+                    image = svg.uiImage
+                }
+                return NCMenuAction(
+                    title: action.title,
+                    icon: image,
+                    action: { _ in self.handleProfileAction(action, for: userId) })
+            }
+
+            let allActions = [personHeader] + actions
+            self.presentMenu(with: allActions)
+        }
+    }
+
+    func sendEmail(to email: String) {
+        guard MFMailComposeViewController.canSendMail() else {
+            NCContentPresenter.shared.showGenericError(description: "_cannot_send_mail_error_")
+            return
+        }
+
+        let mail = MFMailComposeViewController()
+        mail.mailComposeDelegate = self
+        mail.setToRecipients([email])
+
+        present(mail, animated: true)
+    }
+    
+    func presentMenu(with actions: [NCMenuAction]) {
+        let menuViewController = NCMenu.makeNCMenu(with: actions)
+
+        let menuPanelController = NCMenuPanelController()
+        menuPanelController.parentPresenter = self
+        menuPanelController.delegate = menuViewController
+        menuPanelController.set(contentViewController: menuViewController)
+        menuPanelController.track(scrollView: menuViewController.tableView)
+
+        present(menuPanelController, animated: true, completion: nil)
+    }
+}
+
+extension UIViewController: MFMailComposeViewControllerDelegate {
+    public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
+        controller.dismiss(animated: true)
+    }
+}