From 88e2b407594d8a35dba7dd695e0f583d9ae42cdd Mon Sep 17 00:00:00 2001 From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:09:33 +0530 Subject: [PATCH 1/3] NMC 2172 - dashboard theming customisation --- iOSClient/Favorites/NCFavorite.swift | 1 + iOSClient/Files/NCFiles.swift | 4 +- .../Cell/NCCellProtocol.swift | 6 + .../Collection Common/Cell/NCListCell.swift | 166 ++++---- .../Collection Common/Cell/NCListCell.xib | 339 ++++++---------- ...nViewCommon+CollectionViewDataSource.swift | 183 ++++++++- .../NCCollectionViewCommon+SelectTabBar.swift | 126 +++++- ...mmon+SwipeCollectionViewCellDelegate.swift | 94 +++++ .../NCCollectionViewCommon.swift | 366 ++++++++++++++++-- .../NCSectionFirstHeader.swift | 139 ++++++- .../NCSectionFirstHeader.xib | 232 +++++++---- iOSClient/Main/NCActionCenter.swift | 65 ++++ iOSClient/Menu/NCMenuAction.swift | 15 + iOSClient/Menu/NCSortMenu.swift | 149 +++++++ iOSClient/Offline/NCOffline.swift | 1 + 15 files changed, 1462 insertions(+), 424 deletions(-) create mode 100644 iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift create mode 100644 iOSClient/Menu/NCSortMenu.swift diff --git a/iOSClient/Favorites/NCFavorite.swift b/iOSClient/Favorites/NCFavorite.swift index f8d19eac51..0ad520174b 100644 --- a/iOSClient/Favorites/NCFavorite.swift +++ b/iOSClient/Favorites/NCFavorite.swift @@ -32,6 +32,7 @@ class NCFavorite: NCCollectionViewCommon { titleCurrentFolder = NSLocalizedString("_favorites_", comment: "") layoutKey = NCGlobal.shared.layoutViewFavorite enableSearchBar = false + headerMenuButtonsView = true headerRichWorkspaceDisable = true emptyImageName = "star.fill" emptyImageColors = [NCBrandColor.shared.yellowFavorite] diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index 5db54b4da5..5bfd4777a7 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -83,8 +83,8 @@ class NCFiles: NCCollectionViewCommon { } self.titleCurrentFolder = self.getNavigationTitle() - self.navigationItem.title = self.titleCurrentFolder - (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() + ///Magentacloud branding changes hide user account button on left navigation bar +// self.setNavigationLeftItems() self.dataSource.removeAll() self.reloadDataSource() diff --git a/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift b/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift index b861d54f00..9e969faed9 100644 --- a/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift +++ b/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift @@ -39,6 +39,8 @@ protocol NCCellProtocol { var fileSharedImage: UIImageView? { get set } var fileMoreImage: UIImageView? { get set } var cellSeparatorView: UIView? { get set } + var indexPath: IndexPath { get set } + var fileSharedLabel: UILabel? { get set } func titleInfoTrailingDefault() func titleInfoTrailingFull() @@ -115,6 +117,10 @@ extension NCCellProtocol { get { return nil } set {} } + var fileSharedLabel: UILabel? { + get { return nil } + set { } + } func titleInfoTrailingDefault() {} func titleInfoTrailingFull() {} diff --git a/iOSClient/Main/Collection Common/Cell/NCListCell.swift b/iOSClient/Main/Collection Common/Cell/NCListCell.swift index 711fefe711..8066919b13 100755 --- a/iOSClient/Main/Collection Common/Cell/NCListCell.swift +++ b/iOSClient/Main/Collection Common/Cell/NCListCell.swift @@ -28,7 +28,6 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto @IBOutlet weak var imageSelect: UIImageView! @IBOutlet weak var imageStatus: UIImageView! @IBOutlet weak var imageFavorite: UIImageView! - @IBOutlet weak var imageFavoriteBackground: UIImageView! @IBOutlet weak var imageLocal: UIImageView! @IBOutlet weak var labelTitle: UILabel! @IBOutlet weak var labelInfo: UILabel! @@ -38,30 +37,26 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto @IBOutlet weak var buttonShared: UIButton! @IBOutlet weak var imageMore: UIImageView! @IBOutlet weak var buttonMore: UIButton! + @IBOutlet weak var progressView: UIProgressView! @IBOutlet weak var separator: UIView! - @IBOutlet weak var tag0: UILabel! - @IBOutlet weak var tag1: UILabel! - + @IBOutlet weak var labelShared: UILabel! @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint! @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var titleTrailingConstraint: NSLayoutConstraint! + @IBOutlet weak var subInfoTrailingConstraint: NSLayoutConstraint! - var ocId = "" - var ocIdTransfer = "" - var user = "" + private var objectId = "" + private var user = "" + var indexPath = IndexPath() weak var listCellDelegate: NCListCellDelegate? + var namedButtonMore = "" var fileAvatarImageView: UIImageView? { return imageShared } - var fileOcId: String? { - get { return ocId } - set { ocId = newValue ?? "" } - } - var fileOcIdTransfer: String? { - get { return ocIdTransfer } - set { ocIdTransfer = newValue ?? "" } + var fileObjectId: String? { + get { return objectId } + set { objectId = newValue ?? "" } } var filePreviewImageView: UIImageView? { get { return imageItem } @@ -83,6 +78,14 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto get { return labelSubinfo } set { labelSubinfo = newValue } } + var fileProgressView: UIProgressView? { + get { return progressView } + set { progressView = newValue } + } + var fileSelectImage: UIImageView? { + get { return imageSelect } + set { imageSelect = newValue } + } var fileStatusImage: UIImageView? { get { return imageStatus } set { imageStatus = newValue } @@ -108,57 +111,49 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto set { separator = newValue } } - override var accessibilityIdentifier: String? { - get { - super.accessibilityIdentifier - } - set { - super.accessibilityIdentifier = newValue - - if let newValue { - buttonShared.accessibilityIdentifier = "\(newValue)/shareButton" - } - } + var fileSharedLabel: UILabel? { + get { return labelShared } + set { labelShared = newValue } } override func awakeFromNib() { super.awakeFromNib() - initCell() - } - override func prepareForReuse() { - super.prepareForReuse() - initCell() - } + imageItem.layer.cornerRadius = 6 + imageItem.layer.masksToBounds = true - func initCell() { + // use entire cell as accessibility element accessibilityHint = nil accessibilityLabel = nil accessibilityValue = nil isAccessibilityElement = true - imageItem.image = nil - imageItem.layer.cornerRadius = 6 - imageItem.layer.masksToBounds = true - imageStatus.image = nil - imageFavorite.image = nil - imageFavoriteBackground.isHidden = true - imageLocal.image = nil - labelTitle.text = "" - labelInfo.text = "" - labelSubinfo.text = "" - imageShared.image = nil - imageMore.image = nil - separatorHeightConstraint.constant = 0.5 - tag0.text = "" - tag1.text = "" - titleInfoTrailingDefault() + progressView.tintColor = NCBrandColor.shared.brandElement + progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5) + progressView.trackTintColor = .clear let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:))) longPressedGesture.minimumPressDuration = 0.5 longPressedGesture.delegate = self longPressedGesture.delaysTouchesBegan = true self.addGestureRecognizer(longPressedGesture) + + separator.backgroundColor = .separator + separatorHeightConstraint.constant = 0.5 + + labelTitle.text = "" + labelInfo.text = "" + labelTitle.textColor = .label + labelInfo.textColor = .systemGray + labelSubinfo.textColor = .systemGray + } + + override func prepareForReuse() { + super.prepareForReuse() + imageItem.backgroundColor = nil + accessibilityHint = nil + accessibilityLabel = nil + accessibilityValue = nil } override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? { @@ -166,40 +161,43 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } @IBAction func touchUpInsideShare(_ sender: Any) { - listCellDelegate?.tapShareListItem(with: ocId, ocIdTransfer: ocIdTransfer, sender: sender) + listCellDelegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender) } @IBAction func touchUpInsideMore(_ sender: Any) { - listCellDelegate?.tapMoreListItem(with: ocId, ocIdTransfer: ocIdTransfer, image: imageItem.image, sender: sender) + listCellDelegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) } @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - listCellDelegate?.longPressListItem(with: ocId, ocIdTransfer: ocIdTransfer, gestureRecognizer: gestureRecognizer) + listCellDelegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) } fileprivate func setA11yActions() { + let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_" self.accessibilityCustomActions = [ UIAccessibilityCustomAction( name: NSLocalizedString("_share_", comment: ""), target: self, selector: #selector(touchUpInsideShare)), UIAccessibilityCustomAction( - name: NSLocalizedString("_more_", comment: ""), + name: NSLocalizedString(moreName, comment: ""), target: self, selector: #selector(touchUpInsideMore)) ] } func titleInfoTrailingFull() { - titleTrailingConstraint.constant = 10 + subInfoTrailingConstraint.constant = 10 } func titleInfoTrailingDefault() { - titleTrailingConstraint.constant = 90 + subInfoTrailingConstraint.constant = 90 } - func setButtonMore(image: UIImage) { + func setButtonMore(named: String, image: UIImage) { + namedButtonMore = named imageMore.image = image + setA11yActions() } @@ -213,6 +211,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto buttonShared.isHidden = status } + func hideSeparator(_ status: Bool) { + separator.isHidden = status + } + func selected(_ status: Bool, isEditMode: Bool) { if isEditMode { imageItemLeftConstraint.constant = 45 @@ -238,11 +240,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto blurEffectView?.backgroundColor = .lightGray blurEffectView?.frame = self.bounds blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] - imageSelect.image = NCImageCache.shared.getImageCheckedYes() + imageSelect.image = NCImageCache.images.checkedYes backgroundView = blurEffectView separator.isHidden = true } else { - imageSelect.image = NCImageCache.shared.getImageCheckedNo() + imageSelect.image = NCImageCache.images.checkedNo backgroundView = nil separator.isHidden = false } @@ -250,58 +252,20 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } func writeInfoDateSize(date: NSDate, size: Int64) { - labelInfo.text = NCUtility().getRelativeDateTitle(date as Date) - labelSubinfo.text = NCUtilityFileSystem().transformedSize(size) + labelInfo.text = NCUtility().dateDiff(date as Date) + " · " + NCUtilityFileSystem().transformedSize(size) + labelSubinfo.text = "" } func setAccessibility(label: String, value: String) { accessibilityLabel = label accessibilityValue = value } - - func setTags(tags: [String]) { - if tags.isEmpty { - tag0.isHidden = true - tag1.isHidden = true - labelInfo.isHidden = false - labelSubinfo.isHidden = false - } else { - tag0.isHidden = false - tag1.isHidden = true - labelInfo.isHidden = true - labelSubinfo.isHidden = true - - if let tag = tags.first { - tag0.text = tag - if tags.count > 1 { - tag1.isHidden = false - tag1.text = "+\(tags.count - 1)" - } - } - } - } - - func setIconOutlines() { - imageFavoriteBackground.isHidden = fileFavoriteImage?.image == nil - - if imageStatus.image != nil { - imageStatus.makeCircularBackground(withColor: .systemBackground) - } else { - imageStatus.backgroundColor = .clear - } - - if imageLocal.image != nil { - imageLocal.makeCircularBackground(withColor: .systemBackground) - } else { - imageLocal.backgroundColor = .clear - } - } } protocol NCListCellDelegate: AnyObject { - func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) - func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) - func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) + func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) + func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) + func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) } // MARK: - List Layout diff --git a/iOSClient/Main/Collection Common/Cell/NCListCell.xib b/iOSClient/Main/Collection Common/Cell/NCListCell.xib index 0d02c3cfba..a9b542e428 100755 --- a/iOSClient/Main/Collection Common/Cell/NCListCell.xib +++ b/iOSClient/Main/Collection Common/Cell/NCListCell.xib @@ -1,272 +1,191 @@ - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index de8fdc7b9f..bef043ea8b 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -37,10 +37,89 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { self.autoUploadDirectory = self.database.getAccountAutoUploadDirectory(session: self.session) // get layout for view self.layoutForView = self.database.getLayoutForView(account: self.session.account, key: self.layoutKey, serverUrl: self.serverUrl) - + return self.dataSource.numberOfItemsInSection(section) } + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), + let cell = (cell as? NCCellProtocol) else { return } + let existsIcon = utilityFileSystem.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) + + func downloadAvatar(fileName: String, user: String, dispalyName: String?) { + if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) { + cell.fileAvatarImageView?.contentMode = .scaleAspectFill + cell.fileAvatarImageView?.image = image + } else { + NCNetworking.shared.downloadAvatar(user: user, dispalyName: dispalyName, fileName: fileName, cell: cell, view: collectionView) + } + } + /// CONTENT MODE + cell.filePreviewImageView?.layer.borderWidth = 0 + if existsIcon { + cell.filePreviewImageView?.contentMode = .scaleAspectFill + } else { + cell.filePreviewImageView?.contentMode = .scaleAspectFit + } + cell.fileAvatarImageView?.contentMode = .center + /// THUMBNAIL + if !metadata.directory { + if metadata.hasPreviewBorder { + cell.filePreviewImageView?.layer.borderWidth = 0.2 + cell.filePreviewImageView?.layer.borderColor = UIColor.lightGray.cgColor + } + if metadata.name == NCGlobal.shared.appName { + if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare { + if let image = NCImageCache.shared.getPreviewImageCache(ocId: metadata.ocId, etag: metadata.etag) { + cell.filePreviewImageView?.image = image + } else if let image = UIImage(contentsOfFile: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)) { + cell.filePreviewImageView?.image = image + NCImageCache.shared.addPreviewImageCache(metadata: metadata, image: image) + } + } else { + if let image = NCImageCache.shared.getIconImageCache(ocId: metadata.ocId, etag: metadata.etag) { + cell.filePreviewImageView?.image = image + } else if metadata.hasPreview { + cell.filePreviewImageView?.image = utility.getIcon(metadata: metadata) + } + } + if cell.filePreviewImageView?.image == nil { + if metadata.iconName.isEmpty { + cell.filePreviewImageView?.image = NCImageCache.images.file + } else { + cell.filePreviewImageView?.image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true) + } + if metadata.hasPreview && metadata.status == NCGlobal.shared.metadataStatusNormal && !existsIcon { + for case let operation as NCCollectionViewDownloadThumbnail in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { return } + NCNetworking.shared.downloadThumbnailQueue.addOperation(NCCollectionViewDownloadThumbnail(metadata: metadata, cell: cell, collectionView: collectionView)) + } + } + } else { + /// APP NAME - UNIFIED SEARCH + switch metadata.iconName { + case let str where str.contains("contacts"): + cell.filePreviewImageView?.image = NCImageCache.images.iconContacts + case let str where str.contains("conversation"): + cell.filePreviewImageView?.image = NCImageCache.images.iconTalk + case let str where str.contains("calendar"): + cell.filePreviewImageView?.image = NCImageCache.images.iconCalendar + case let str where str.contains("deck"): + cell.filePreviewImageView?.image = NCImageCache.images.iconDeck + case let str where str.contains("mail"): + cell.filePreviewImageView?.image = NCImageCache.images.iconMail + case let str where str.contains("talk"): + cell.filePreviewImageView?.image = NCImageCache.images.iconTalk + case let str where str.contains("confirm"): + cell.filePreviewImageView?.image = NCImageCache.images.iconConfirm + case let str where str.contains("pages"): + cell.filePreviewImageView?.image = NCImageCache.images.iconPages + default: + cell.filePreviewImageView?.image = NCImageCache.images.iconFile + } + } + } + } + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if !collectionView.indexPathsForVisibleItems.contains(indexPath) { guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return } @@ -133,9 +212,25 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { var isShare = false var isMounted = false var a11yValues: [String] = [] + + // LAYOUT PHOTO + if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare { + guard let photoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? NCPhotoCell else { return NCPhotoCell() } + photoCell.photoCellDelegate = self + cell = photoCell + } else if layoutForView?.layout == NCGlobal.shared.layoutGrid { + // LAYOUT GRID + guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return NCGridCell() } + gridCell.gridCellDelegate = self + cell = gridCell + } else { + // LAYOUT LIST + guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return NCListCell() } + listCell.listCellDelegate = self + cell = listCell + } let metadata = self.dataSource.getMetadata(indexPath: indexPath) ?? tableMetadata() - let existsImagePreview = utilityFileSystem.fileProviderStorageImageExists(metadata.ocId, etag: metadata.etag) - let ext = global.getSizeExtension(column: self.numberOfColumns) + let shares = NCManageDatabase.shared.getTableShares(metadata: metadata) defer { if !metadata.isSharable() || NCCapabilities.shared.disableSharesView(account: metadata.account) { @@ -327,14 +422,28 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } // Share image - if isShare { - cell.fileSharedImage?.image = imageCache.getImageShared() - } else if !metadata.shareType.isEmpty { - metadata.shareType.contains(3) ? - (cell.fileSharedImage?.image = imageCache.getImageShareByLink()) : - (cell.fileSharedImage?.image = imageCache.getImageShared()) + if isShare || !metadata.shareType.isEmpty { + cell.fileSharedImage?.image = NCImageCache.images.shared } else { - cell.fileSharedImage?.image = imageCache.getImageCanShare() + cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) + cell.fileSharedLabel?.text = "" + } + if appDelegate.account != metadata.account { + cell.fileSharedImage?.image = NCImageCache.images.shared + } + cell.fileSharedLabel?.text = NSLocalizedString("_shared_", comment: "") + cell.fileSharedLabel?.textColor = NCBrandColor.shared.customer + if (!metadata.shareType.isEmpty || !(shares.share?.isEmpty ?? true) || (shares.firstShareLink != nil)){ + cell.fileSharedImage?.image = cell.fileSharedImage?.image?.imageColor(NCBrandColor.shared.customer) + } else { + cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) + cell.fileSharedLabel?.text = "" + } + + if metadata.permissions.contains("S"), (metadata.permissions.range(of: "S") != nil) { + cell.fileSharedImage?.image = NCImageCache.images.sharedWithMe + cell.fileSharedLabel?.text = NSLocalizedString("_recieved_", comment: "") + cell.fileSharedLabel?.textColor = NCBrandColor.shared.notificationAction } // Button More @@ -477,8 +586,14 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.setIconOutlines() - cell.accessibilityLabel = metadata.fileName - cell.accessibilityIdentifier = "Cell/\(metadata.fileName)" + // Hide lines on iPhone + if !UIDevice.current.orientation.isLandscape && UIDevice.current.model.hasPrefix("iPhone") { + cell.cellSeparatorView?.isHidden = true + cell.fileSharedLabel?.isHidden = true + }else{ + cell.cellSeparatorView?.isHidden = false + cell.fileSharedLabel?.isHidden = false + } return cell } @@ -487,6 +602,15 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { func setContent(header: UICollectionReusableView, indexPath: IndexPath) { let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: indexPath.section) + if kind == UICollectionView.elementKindSectionHeader { + + if dataSource.getMetadataSourceForAllSections().isEmpty { + + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } + self.sectionFirstHeaderEmptyData = header + header.delegate = self + + } if let header = header as? NCSectionFirstHeader { let recommendations = self.database.getRecommendedFiles(account: self.session.account) var sectionText = NSLocalizedString("_all_files_", comment: "") @@ -569,7 +693,40 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } self.sectionFirstHeader = header - setContent(header: header, indexPath: indexPath) +// setContent(header: header, indexPath: indexPath) + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + header.setImageSwitchList() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") + } else { + header.setImageSwitchGrid() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") + } + header.delegate = self + + if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { + header.setViewTransfer(isHidden: false) + } else { + header.setViewTransfer(isHidden: true) + } + + if headerMenuButtonsView { + header.setStatusButtonsView(enable: !dataSource.getMetadataSourceForAllSections().isEmpty) + header.setButtonsView(height: NCGlobal.shared.heightButtonsView) + header.setSortedTitle(layoutForView?.titleButtonHeader ?? "") + } else { + header.setButtonsView(height: 0) + } + + header.setRichWorkspaceHeight(heightHeaderRichWorkspace) + header.setRichWorkspaceText(richWorkspaceText) + + header.setSectionHeight(heightHeaderSection) + if heightHeaderSection == 0 { + header.labelSection.text = "" + } else { + header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) + } + header.labelSection.textColor = NCBrandColor.shared.textColor return header diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift index bfabead0bf..13c3b96dfa 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift @@ -132,7 +132,8 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { if editMode { navigationItem.leftBarButtonItems = nil } else { - (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() + ///Magentacloud branding changes hide user account button on left navigation bar +// setNavigationLeftItems() } (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() @@ -150,4 +151,127 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { } setEditMode(false) } + + /// If explicit `isOn` is not set, it will invert `isEditMode` + func toggleSelect(isOn: Bool? = nil) { + DispatchQueue.main.async { + self.isEditMode = isOn ?? !self.isEditMode + self.selectOcId.removeAll() + self.setNavigationLeftItems() + self.setNavigationRightItems() + self.collectionView.reloadData() + } + } + + func createMenuActions() -> [NCMenuAction] { + var actions = [NCMenuAction]() + + actions.append(.cancelAction { + self.toggleSelect() + }) + if selectOcId.count != dataSource.getMetadataSourceForAllSections().count { + actions.append(.selectAllAction(action: selectAll)) + } + + guard !selectOcId.isEmpty else { return actions } + + actions.append(.seperator(order: 0)) + + var selectedMetadatas: [tableMetadata] = [] + var selectedMediaMetadatas: [tableMetadata] = [] + var isAnyOffline = false + var isAnyFolder = false + var isAnyLocked = false + var canUnlock = true + var canOpenIn = false + var isDirectoryE2EE = false + + for ocId in selectOcId { + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue } + if metadata.e2eEncrypted { + selectOcId.removeAll(where: {$0 == metadata.ocId}) + } else { + selectedMetadatas.append(metadata) + } + + if [NKCommon.TypeClassFile.image.rawValue, NKCommon.TypeClassFile.video.rawValue].contains(metadata.classFile) { + selectedMediaMetadatas.append(metadata) + } + if metadata.directory { isAnyFolder = true } + if metadata.lock { + isAnyLocked = true + if metadata.lockOwner != appDelegate.userId { + canUnlock = false + } + } + + guard !isAnyOffline else { continue } + if metadata.directory, + let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) { + isAnyOffline = directory.offline + } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) { + isAnyOffline = localFile.offline + } // else: file is not offline, continue + + if !metadata.directory { + canOpenIn = true + } + + if metadata.isDirectoryE2EE { + isDirectoryE2EE = true + } + } + + if canOpenIn { + actions.append(.share(selectedMetadatas: selectedMetadatas, viewController: self, completion: { self.toggleSelect() })) + } + + if !isAnyFolder, canUnlock, !NCGlobal.shared.capabilityFilesLockVersion.isEmpty { + actions.append(.lockUnlockFiles(shouldLock: !isAnyLocked, metadatas: selectedMetadatas, completion: { self.toggleSelect() })) + } + + if !selectedMediaMetadatas.isEmpty { + var title: String = NSLocalizedString("_save_selected_files_", comment: "") + var icon = NCUtility().loadImage(named: "save_files",colors: [NCBrandColor.shared.iconImageColor]) + if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) { + title = NSLocalizedString("_livephoto_save_", comment: "") + icon = NCUtility().loadImage(named: "livephoto") + } + + actions.append(NCMenuAction( + title: title, + icon: icon, + order: 0, + action: { _ in + for metadata in selectedMediaMetadatas { + if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) { + NCNetworking.shared.saveLivePhotoQueue.addOperation(NCOperationSaveLivePhoto(metadata: metadata, metadataMOV: metadataMOV, hudView: self.view)) + } else { + if NCUtilityFileSystem().fileProviderStorageExists(metadata) { + NCActionCenter.shared.saveAlbum(metadata: metadata, controller: self.tabBarController as? NCMainTabBarController) + } else { + if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty { + NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)) + } + } + } + } + self.toggleSelect() + } + ) + ) + } + actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: { + self.reloadDataSource() + self.toggleSelect() + })) + + if !isDirectoryE2EE { + actions.append(.moveOrCopyAction(selectedMetadatas: selectedMetadatas, viewController: self, indexPath: [], completion: { self.toggleSelect() })) + actions.append(.copyAction(selectOcId: selectOcId, viewController: self, completion: { self.toggleSelect() })) + } + actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, indexPaths: [], viewController: self, completion: { self.toggleSelect() })) + return actions + } + } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift new file mode 100644 index 0000000000..11a580f6a3 --- /dev/null +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift @@ -0,0 +1,94 @@ +// +// NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift +// Nextcloud +// +// Created by Milen on 01.03.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import Foundation +import SwipeCellKit + +extension NCCollectionViewCommon: SwipeCollectionViewCellDelegate { + func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeCellKit.SwipeActionsOrientation) -> [SwipeCellKit.SwipeAction]? { + guard orientation == .right, let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return nil } + + let scaleTransition = ScaleTransition(duration: 0.3, initialScale: 0.8, threshold: 0.8) + + // wait a fix for truncate the text .. ? .. + let favoriteAction = SwipeAction(style: .default, title: NSLocalizedString(metadata.favorite ? "_favorite_short_" : "_favorite_short_", comment: "") ) { _, _ in + NCNetworking.shared.favoriteMetadata(metadata) { error in + if error != .success { + NCContentPresenter().showError(error: error) + } + } + } + favoriteAction.backgroundColor = NCBrandColor.shared.yellowFavorite + favoriteAction.image = .init(systemName: "star.fill") + favoriteAction.transitionDelegate = scaleTransition + favoriteAction.hidesWhenSelected = true + + var actions = [favoriteAction] + + let shareAction = SwipeAction(style: .default, title: NSLocalizedString("_share_", comment: "")) { _, _ in + NCActionCenter.shared.openActivityViewController(selectedMetadata: [metadata]) + } + shareAction.backgroundColor = .blue + shareAction.image = .init(systemName: "square.and.arrow.up") + shareAction.transitionDelegate = scaleTransition + shareAction.hidesWhenSelected = true + + let deleteAction = SwipeAction(style: .destructive, title: NSLocalizedString("_delete_", comment: "")) { _, _ in + let titleDelete: String + + if metadata.directory { + titleDelete = NSLocalizedString("_delete_folder_", comment: "") + } else { + titleDelete = NSLocalizedString("_delete_file_", comment: "") + } + + let message = NSLocalizedString("_want_delete_", comment: "") + "\n - " + metadata.fileNameView + + let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message, canDeleteServer: !metadata.lock, selectedMetadatas: [metadata], indexPaths: self.selectIndexPaths) { _ in } + + self.viewController.present(alertController, animated: true, completion: nil) + } + deleteAction.image = .init(systemName: "trash") + deleteAction.style = .destructive + deleteAction.transitionDelegate = scaleTransition + deleteAction.hidesWhenSelected = true + + if !NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) { + actions.insert(deleteAction, at: 0) + } + + if metadata.canShare { + actions.append(shareAction) + } + + return actions + } + + func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions { + var options = SwipeOptions() + options.expansionStyle = .selection + options.transitionStyle = .border + options.backgroundColor = .clear + return options + } +} diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index 76a15817f2..e3d8056199 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -67,6 +67,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var tabBarSelect: NCCollectionViewCommonSelectTabBar? var attributesZoomIn: UIMenuElement.Attributes = [] var attributesZoomOut: UIMenuElement.Attributes = [] + let maxImageGrid: CGFloat = 7 + var headerMenu: NCSectionFirstHeader? var tipViewAccounts: EasyTipView? var tipViewAutoUpload: EasyTipView? @@ -80,6 +82,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var emptyImageName: String? var emptyImageColors: [UIColor]? + var headerMenuButtonsView: Bool = true + var emptyImage: UIImage? var emptyTitle: String = "" var emptyDescription: String = "" @@ -255,8 +259,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS isEditMode = false - (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() - (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() + /// Magentacloud branding changes hide user account button on left navigation bar +// setNavigationLeftItems() + setNavigationRightItems() layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) if isLayoutList { @@ -381,6 +386,17 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS self.refreshControl.endRefreshing() } + @objc func reloadAvatar(_ notification: NSNotification) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.showTip() + } + guard let userInfo = notification.userInfo as NSDictionary?, + let error = userInfo["error"] as? NKError, + error.errorCode != global.errorNotModified else { return } + /// Magentacloud branding changes hide user account button on left navigation bar +// setNavigationLeftItems() + } + @objc func changeTheming(_ notification: NSNotification) { self.reloadDataSource() } @@ -673,6 +689,286 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - Layout + func setNavigationLeftItems() { + guard layoutKey == global.layoutViewFiles, + let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) else { + return } + let image = utility.loadUserImage(for: tableAccount.user, displayName: tableAccount.displayName, urlBase: tableAccount.urlBase) + let accountButton = AccountSwitcherButton(type: .custom) + let accounts = database.getAllAccountOrderAlias() + var childrenAccountSubmenu: [UIMenuElement] = [] + + accountButton.setImage(image, for: .normal) + accountButton.setImage(image, for: .highlighted) + accountButton.semanticContentAttribute = .forceLeftToRight + accountButton.sizeToFit() + + if !accounts.isEmpty { + let accountActions: [UIAction] = accounts.map { account in + let image = utility.loadUserImage(for: account.user, displayName: account.displayName, urlBase: account.urlBase) + var name: String = "" + var url: String = "" + + if account.alias.isEmpty { + name = account.displayName + url = (URL(string: account.urlBase)?.host ?? "") + } else { + name = account.alias + } + + let action = UIAction(title: name, image: image, state: account.active ? .on : .off) { _ in + if !account.active { + NCAccount().changeAccount(account.account, userProfile: nil, controller: self.controller) { } + self.setEditMode(false) + } + } + + action.subtitle = url + return action + } + + let addAccountAction = UIAction(title: NSLocalizedString("_add_account_", comment: ""), image: utility.loadImage(named: "person.crop.circle.badge.plus", colors: NCBrandColor.shared.iconImageMultiColors)) { _ in + self.appDelegate.openLogin(selector: self.global.introLogin) + } + + let settingsAccountAction = UIAction(title: NSLocalizedString("_account_settings_", comment: ""), image: utility.loadImage(named: "gear", colors: [NCBrandColor.shared.iconImageColor])) { _ in + let accountSettingsModel = NCAccountSettingsModel(controller: self.controller, delegate: self) + let accountSettingsView = NCAccountSettingsView(model: accountSettingsModel) + let accountSettingsController = UIHostingController(rootView: accountSettingsView) + self.present(accountSettingsController, animated: true, completion: nil) + } + + if !NCBrandOptions.shared.disable_multiaccount { + childrenAccountSubmenu.append(addAccountAction) + } + childrenAccountSubmenu.append(settingsAccountAction) + + let addAccountSubmenu = UIMenu(title: "", options: .displayInline, children: childrenAccountSubmenu) + let menu = UIMenu(children: accountActions + [addAccountSubmenu]) + + accountButton.menu = menu + accountButton.showsMenuAsPrimaryAction = true + + accountButton.onMenuOpened = { + self.dismissTip() + } + } + + navigationItem.leftItemsSupplementBackButton = true + navigationItem.setLeftBarButtonItems([UIBarButtonItem(customView: accountButton)], animated: true) + + if titlePreviusFolder != nil { + navigationController?.navigationBar.topItem?.title = titlePreviusFolder + } + + navigationItem.title = titleCurrentFolder + } + + func setNavigationRightItems() { + guard layoutKey != global.layoutViewTransfers else { return } + let isTabBarHidden = self.tabBarController?.tabBar.isHidden ?? true + let isTabBarSelectHidden = tabBarSelect.isHidden() + + func createMenuActions() -> [UIMenuElement] { + guard let layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return [] } + + let select = UIAction(title: NSLocalizedString("_select_", comment: ""), + image: utility.loadImage(named: "checkmark.circle"), + attributes: (self.dataSource.isEmpty() || NCNetworking.shared.isOffline) ? .disabled : []) { _ in + self.setEditMode(true) + self.collectionView.reloadData() + } + + let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: utility.loadImage(named: "list.bullet"), state: layoutForView.layout == global.layoutList ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutList + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: utility.loadImage(named: "square.grid.2x2"), state: layoutForView.layout == global.layoutGrid ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutGrid + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let mediaSquare = UIAction(title: NSLocalizedString("_media_square_", comment: ""), image: utility.loadImage(named: "square.grid.3x3"), state: layoutForView.layout == global.layoutPhotoSquare ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutPhotoSquare + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let mediaRatio = UIAction(title: NSLocalizedString("_media_ratio_", comment: ""), image: utility.loadImage(named: "rectangle.grid.3x2"), state: layoutForView.layout == self.global.layoutPhotoRatio ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutPhotoRatio + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid, mediaSquare, mediaRatio]) + + let ascending = layoutForView.ascending + let ascendingChevronImage = utility.loadImage(named: ascending ? "chevron.up" : "chevron.down") + let isName = layoutForView.sort == "fileName" + let isDate = layoutForView.sort == "date" + let isSize = layoutForView.sort == "size" + + let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in + + if isName { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "fileName" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in + + if isDate { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "date" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in + + if isSize { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "size" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest]) + + let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: utility.loadImage(named: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in + + layoutForView.directoryOnTop = !layoutForView.directoryOnTop + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) + } + + let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: session.account) + let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: utility.loadImage(named: "folder.badge.person.crop", colors: NCBrandColor.shared.iconImageMultiColors), state: personalFilesOnly ? .on : .off) { _ in + + NCKeychain().setPersonalFilesOnly(account: self.session.account, value: !personalFilesOnly) + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) + self.setNavigationRightItems() + } + + let showDescriptionKeychain = NCKeychain().showDescription + let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: utility.loadImage(named: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in + + NCKeychain().showDescription = !showDescriptionKeychain + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) + self.setNavigationRightItems() + } + showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" + + if layoutKey == global.layoutViewRecent { + return [select] + } else { + var additionalSubmenu = UIMenu() + if layoutKey == global.layoutViewFiles { + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription]) + } else { + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription]) + } + return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] + } + } + + if isEditMode { + /// Magentacloud branding changes hide options on bottom tab bar +// tabBarSelect.update(selectOcId: selectOcId, metadatas: getSelectedMetadatas(), userId: appDelegate.userId) +// tabBarSelect.show() + let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { + self.setEditMode(false) + self.collectionView.reloadData() + } + navigationItem.rightBarButtonItems = [select] + } else if navigationItem.rightBarButtonItems == nil || (!isEditMode && !tabBarSelect.isHidden()) { + /// Magentacloud branding changes hide options on bottom tab bar +// tabBarSelect.hide() + let menuButton = UIBarButtonItem(image: utility.loadImage(named: "ellipsis.circle"), menu: UIMenu(children: createMenuActions())) + menuButton.tintColor = NCBrandColor.shared.iconImageColor + if layoutKey == global.layoutViewFiles { + let notification = UIBarButtonItem(image: utility.loadImage(named: "bell"), style: .plain) { + if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { + viewController.session = self.session + self.navigationController?.pushViewController(viewController, animated: true) + } + } + notification.tintColor = NCBrandColor.shared.iconImageColor + navigationItem.rightBarButtonItems = [menuButton, notification] + } else { + navigationItem.rightBarButtonItems = [menuButton] + } + } else { + navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions()) + } + + if isEditMode { + let more = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain) { self.presentMenu(with: self.createMenuActions())} + navigationItem.rightBarButtonItems = [more] + } else { + let select = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain) { self.toggleSelect() } + if layoutKey == NCGlobal.shared.layoutViewFiles { + let notification = UIBarButtonItem(image: utility.loadImage(named: "bell"), style: .plain) { + if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { + self.navigationController?.pushViewController(viewController, animated: true) + } + } + notification.tintColor = NCBrandColor.shared.iconImageColor + navigationItem.rightBarButtonItems = [select, notification] + } else { + navigationItem.rightBarButtonItems = [select] + } + } + guard layoutKey == NCGlobal.shared.layoutViewFiles else { return } + navigationItem.title = titleCurrentFolder + } + func getNavigationTitle() -> String { let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) if let tableAccount, @@ -773,7 +1069,30 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } - func longPressGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } + func tapButtonSwitch(_ sender: Any) { + guard !isTransitioning else { return } + isTransitioning = true + + guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return } + + if layoutForView.layout == NCGlobal.shared.layoutGrid { + layoutForView.layout = NCGlobal.shared.layoutList + } else { + layoutForView.layout = NCGlobal.shared.layoutGrid + } + self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView) + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false } + } + + func tapButtonOrder(_ sender: Any) { + let sortMenu = NCSortMenu() + sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) + } + + func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { + } func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -953,28 +1272,31 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - Header size - func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat, - heightHeaderRecommendations: CGFloat, - heightHeaderSection: CGFloat) { - var heightHeaderRichWorkspace: CGFloat = 0 - var heightHeaderRecommendations: CGFloat = 0 - var heightHeaderSection: CGFloat = 0 - - if showDescription, - !isSearchingMode, - let richWorkspaceText = self.richWorkspaceText, - !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty { - heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6 - } + func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) { + var headerRichWorkspace: CGFloat = 0 - if isRecommendationActived, - !isSearchingMode, - NCKeychain().showRecommendedFiles, - !self.database.getRecommendedFiles(account: self.session.account).isEmpty { - heightHeaderRecommendations = self.heightHeaderRecommendations - heightHeaderSection = self.heightHeaderSection + func getHeaderHeight() -> CGFloat { + var size: CGFloat = 0 + + if isHeaderMenuTransferViewEnabled() != nil { + if !isSearchingMode { + size += global.heightHeaderTransfer + } + } + if headerMenuButtonsView { + size += NCGlobal.shared.heightButtonsView + } + return size } +// if isRecommendationActived, +// !isSearchingMode, +// NCKeychain().showRecommendedFiles, +// !self.database.getRecommendedFiles(account: self.session.account).isEmpty { +// heightHeaderRecommendations = self.heightHeaderRecommendations +// heightHeaderSection = self.heightHeaderSection +// } + if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 { if section == 0 { return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection) diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift index 254cf41298..bd30492ed0 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift @@ -26,16 +26,40 @@ import MarkdownKit import NextcloudKit protocol NCSectionFirstHeaderDelegate: AnyObject { + func tapButtonSwitch(_ sender: Any) + func tapButtonOrder(_ sender: Any) + func tapButtonTransfer(_ sender: Any) func tapRichWorkspace(_ sender: Any) func tapRecommendations(with metadata: tableMetadata) func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) } +extension NCSectionFirstHeaderDelegate { + func tapButtonSwitch(_ sender: Any) {} + func tapButtonOrder(_ sender: Any) {} +} + class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegate { + + @IBOutlet weak var buttonSwitch: UIButton! + @IBOutlet weak var buttonOrder: UIButton! + @IBOutlet weak var buttonTransfer: UIButton! + @IBOutlet weak var imageButtonTransfer: UIImageView! + @IBOutlet weak var labelTransfer: UILabel! + @IBOutlet weak var progressTransfer: UIProgressView! + @IBOutlet weak var transferSeparatorBottom: UIView! + @IBOutlet weak var textViewRichWorkspace: UITextView! + @IBOutlet weak var labelSection: UILabel! + @IBOutlet weak var viewTransfer: UIView! @IBOutlet weak var viewRichWorkspace: UIView! @IBOutlet weak var viewRecommendations: UIView! @IBOutlet weak var viewSection: UIView! + @IBOutlet weak var viewButtonsView: UIView! + @IBOutlet weak var viewSeparator: UIView! + @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var viewButtonsViewHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var viewSeparatorHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewRecommendationsHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint! @@ -62,6 +86,18 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat richWorkspaceGradient.startPoint = CGPoint(x: 0, y: 0.8) richWorkspaceGradient.endPoint = CGPoint(x: 0, y: 0.9) viewRichWorkspace.layer.addSublayer(richWorkspaceGradient) + backgroundColor = .clear + + //Button + buttonSwitch.setImage(UIImage(systemName: "list.bullet"), for: .normal)//!.image(color: NCBrandColor.shared.iconColor, size: 25), for: .normal) + + buttonOrder.setTitle("", for: .normal) + buttonOrder.setTitleColor(NCBrandColor.shared.brand, for: .normal) + + // Gradient +// gradient.startPoint = CGPoint(x: 0, y: 0.8) +// gradient.endPoint = CGPoint(x: 0, y: 0.9) +// viewRichWorkspace.layer.addSublayer(gradient) let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:))) tap.delegate = self @@ -90,6 +126,19 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat // labelSection.text = "" viewSectionHeightConstraint.constant = 0 + + buttonTransfer.backgroundColor = .clear + buttonTransfer.setImage(nil, for: .normal) + buttonTransfer.layer.cornerRadius = 6 + buttonTransfer.layer.masksToBounds = true + imageButtonTransfer.image = NCUtility().loadImage(named: "stop.circle") + imageButtonTransfer.tintColor = .white + labelTransfer.text = "" + progressTransfer.progress = 0 + progressTransfer.tintColor = NCBrandColor.shared.brandElement + progressTransfer.trackTintColor = NCBrandColor.shared.brandElement.withAlphaComponent(0.2) + transferSeparatorBottom.backgroundColor = .separator + transferSeparatorBottomHeightConstraint.constant = 0.5 } override func layoutSublayers(of layer: CALayer) { @@ -105,6 +154,41 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat setRichWorkspaceColor() } + // MARK: - View + + func setStatusButtonsView(enable: Bool) { + + buttonSwitch.isEnabled = enable + buttonOrder.isEnabled = enable + } + + func setImageSwitchList() { + + buttonSwitch.setImage(UIImage(systemName: "list.bullet"), for: .normal)//!.image(color: NCBrandColor.shared.iconColor, width: 20, height: 15), for: .normal) + } + + func setImageSwitchGrid() { + + buttonSwitch.setImage(UIImage(systemName: "square.grid.2x2")!.image(color: NCBrandColor.shared.iconImageColor, size: 20), for: .normal) + } + + func setButtonsView(height: CGFloat) { + + viewButtonsViewHeightConstraint.constant = height + if height == 0 { + viewButtonsView.isHidden = true + } else { + viewButtonsView.isHidden = false + } + } + + func setSortedTitle(_ title: String) { + + let title = NSLocalizedString(title, comment: "") + buttonOrder.setTitle(title, for: .normal) + } + + // MARK: - RichWorkspace func setContent(heightHeaderRichWorkspace: CGFloat, richWorkspaceText: String?, heightHeaderRecommendations: CGFloat, @@ -116,7 +200,7 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat viewRichWorkspaceHeightConstraint.constant = heightHeaderRichWorkspace viewRecommendationsHeightConstraint.constant = heightHeaderRecommendations viewSectionHeightConstraint.constant = heightHeaderSection - + if let richWorkspaceText, richWorkspaceText != self.richWorkspaceText { textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) self.richWorkspaceText = richWorkspaceText @@ -126,17 +210,48 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat self.labelSection.text = sectionText self.viewController = viewController self.delegate = delegate - + if heightHeaderRichWorkspace != 0, let richWorkspaceText, !richWorkspaceText.isEmpty { viewRichWorkspace.isHidden = false } else { viewRichWorkspace.isHidden = true } + } + + func setRichWorkspaceText(_ text: String?) { + guard let text = text else { return } + + if text != self.richWorkspaceText { + textViewRichWorkspace.attributedText = markdownParser.parse(text) + self.richWorkspaceText = text + } + } - if heightHeaderRecommendations != 0 && !recommendations.isEmpty { - viewRecommendations.isHidden = false + // MARK: - Transfer + + func setViewTransfer(isHidden: Bool, ocId: String? = nil, text: String? = nil, progress: Float? = nil) { + labelTransfer.text = text + viewTransfer.isHidden = isHidden + progressTransfer.progress = 0 + + if isHidden { + viewTransferHeightConstraint.constant = 0 } else { - viewRecommendations.isHidden = true + var image: UIImage? + if let ocId, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + image = utility.getIcon(metadata: metadata)?.darken() + if image == nil { + image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true) + buttonTransfer.backgroundColor = .lightGray + } else { + buttonTransfer.backgroundColor = .clear + } + } + viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer + if let progress { + progressTransfer.progress = progress + } } if heightHeaderSection == 0 { @@ -157,6 +272,20 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] } } + + // MARK: - Action + + @IBAction func touchUpInsideSwitch(_ sender: Any) { + delegate?.tapButtonSwitch(sender) + } + + @IBAction func touchUpInsideOrder(_ sender: Any) { + delegate?.tapButtonOrder(sender) + } + + @IBAction func touchUpTransfer(_ sender: Any) { + delegate?.tapButtonTransfer(sender) + } @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { delegate?.tapRichWorkspace(sender) diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib index 1206a44a6a..528b2f0098 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib @@ -1,24 +1,76 @@ - + - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -26,98 +78,138 @@ - - - - - + + + + + - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + - - + + - - - - - + + + + + - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + + + + - - + + diff --git a/iOSClient/Main/NCActionCenter.swift b/iOSClient/Main/NCActionCenter.swift index f777147968..64dd9ce754 100644 --- a/iOSClient/Main/NCActionCenter.swift +++ b/iOSClient/Main/NCActionCenter.swift @@ -455,6 +455,55 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } + + // MARK: - Copy & Paste + + func copyPasteboard(pasteboardOcIds: [String], viewController: UIViewController) { + var items = [[String: Any]]() + guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } + let hudView = viewController.view + var fractionCompleted: Float = 0 + + // getting file data can take some time and block the main queue + DispatchQueue.global(qos: .userInitiated).async { + var downloadMetadatas: [tableMetadata] = [] + for ocid in pasteboardOcIds { + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocid) else { continue } + if let pasteboardItem = metadata.toPasteBoardItem() { + items.append(pasteboardItem) + } else { + downloadMetadatas.append(metadata) + } + } + + // do 5 downloads in parallel to optimize efficiency + let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, hudView: hudView) + + for metadata in downloadMetadatas { + processor.execute { completion in + guard let metadata = NCManageDatabase.shared.setMetadatasSessionInWaitDownload(metadatas: [metadata], + session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload, + selector: "") else { return completion() } + NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: false) { + } requestHandler: { _ in + } progressHandler: { progress in + if Float(progress.fractionCompleted) > fractionCompleted || fractionCompleted == 0 { + processor.hud?.progress = Float(progress.fractionCompleted) + fractionCompleted = Float(progress.fractionCompleted) + } + } completion: { _, _ in + fractionCompleted = 0 + completion() + } + } + } + processor.completeWork { + items.append(contentsOf: downloadMetadatas.compactMap({ $0.toPasteBoardItem() })) + UIPasteboard.general.setItems(items, options: [:]) + } + } + } + // MARK: - Copy & Paste @@ -665,3 +714,19 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } } + + +fileprivate extension tableMetadata { + func toPasteBoardItem() -> [String: Any]? { + // Get Data + let fileUrl = URL(fileURLWithPath: NCUtilityFileSystem().getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView)) + guard NCUtilityFileSystem().fileProviderStorageExists(self), + let data = try? Data(contentsOf: fileUrl) else { return nil } + + // Determine the UTI for the file + guard let fileUTI = UTType(filenameExtension: fileExtension)?.identifier else { return nil } + + // Pasteboard item + return [fileUTI: data] + } +} diff --git a/iOSClient/Menu/NCMenuAction.swift b/iOSClient/Menu/NCMenuAction.swift index 3e5d733b26..691164f308 100644 --- a/iOSClient/Menu/NCMenuAction.swift +++ b/iOSClient/Menu/NCMenuAction.swift @@ -195,6 +195,21 @@ extension NCMenuAction { } ) } + + /// Copy files to pasteboard + static func copyAction(selectOcId: [String], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { + NCMenuAction( + title: NSLocalizedString("_copy_file_", comment: ""), + icon: NCUtility().loadImage(named: "copy", colors: [NCBrandColor.shared.iconImageColor]), + order: order, + action: { _ in + NCActionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId, viewController: viewController) + completion?() + } + ) + } + + /// Open view that lets the user move or copy the files within Nextcloud static func moveOrCopyAction(selectedMetadatas: [tableMetadata], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { NCMenuAction( diff --git a/iOSClient/Menu/NCSortMenu.swift b/iOSClient/Menu/NCSortMenu.swift new file mode 100644 index 0000000000..3aaadb4dcd --- /dev/null +++ b/iOSClient/Menu/NCSortMenu.swift @@ -0,0 +1,149 @@ +// +// NCSortMenu.swift +// Nextcloud +// +// Created by Marino Faggiana on 27/08/2020. +// Copyright © 2020 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import UIKit +import FloatingPanel +import NextcloudKit + +class NCSortMenu: NSObject { + + private var sortButton: UIButton? + private var serverUrl: String = "" + private var hideDirectoryOnTop: Bool? + + private var key = "" + + func toggleMenu(viewController: UIViewController, account: String, key: String, sortButton: UIButton?, serverUrl: String, hideDirectoryOnTop: Bool = false) { + + self.key = key + self.sortButton = sortButton + self.serverUrl = serverUrl + self.hideDirectoryOnTop = hideDirectoryOnTop + + guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: account, key: key, serverUrl: serverUrl) else { return } + var actions = [NCMenuAction]() + var title = "" + var icon = UIImage() + + if layoutForView.ascending { + title = NSLocalizedString("_order_by_name_z_a_", comment: "") + icon = UIImage(named: "sortFileNameZA")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } else { + title = NSLocalizedString("_order_by_name_a_z_", comment: "") + icon = UIImage(named: "sortFileNameAZ")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } + + actions.append( + NCMenuAction( + title: title, + icon: icon, + selected: layoutForView.sort == "fileName", + on: layoutForView.sort == "fileName", + action: { _ in + layoutForView.sort = "fileName" + layoutForView.ascending = !layoutForView.ascending + self.actionMenu(layoutForView: layoutForView) + } + ) + ) + + if layoutForView.ascending { + title = NSLocalizedString("_order_by_date_more_recent_", comment: "") + icon = UIImage(named: "sortDateMoreRecent")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } else { + title = NSLocalizedString("_order_by_date_less_recent_", comment: "") + icon = UIImage(named: "sortDateLessRecent")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } + + actions.append( + NCMenuAction( + title: title, + icon: icon, + selected: layoutForView.sort == "date", + on: layoutForView.sort == "date", + action: { _ in + layoutForView.sort = "date" + layoutForView.ascending = !layoutForView.ascending + self.actionMenu(layoutForView: layoutForView) + } + ) + ) + + if layoutForView.ascending { + title = NSLocalizedString("_order_by_size_largest_", comment: "") + icon = UIImage(named: "sortLargest")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } else { + title = NSLocalizedString("_order_by_size_smallest_", comment: "") + icon = UIImage(named: "sortSmallest")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + } + + actions.append( + NCMenuAction( + title: title, + icon: icon, + selected: layoutForView.sort == "size", + on: layoutForView.sort == "size", + action: { _ in + layoutForView.sort = "size" + layoutForView.ascending = !layoutForView.ascending + self.actionMenu(layoutForView: layoutForView) + } + ) + ) + + if !hideDirectoryOnTop { + actions.append( + NCMenuAction( + title: NSLocalizedString("_directory_on_top_no_", comment: ""), + icon: UIImage(named: "foldersOnTop")!.image(color: NCBrandColor.shared.iconImageColor, size: 50), + selected: layoutForView.directoryOnTop, + on: layoutForView.directoryOnTop, + action: { _ in + layoutForView.directoryOnTop = !layoutForView.directoryOnTop + self.actionMenu(layoutForView: layoutForView) + } + ) + ) + } + + viewController.presentMenu(with: actions) + } + + func actionMenu(layoutForView: NCDBLayoutForView) { + + switch layoutForView.sort { + case "fileName": + layoutForView.titleButtonHeader = layoutForView.ascending ? "_sorted_by_name_a_z_" : "_sorted_by_name_z_a_" + case "date": + layoutForView.titleButtonHeader = layoutForView.ascending ? "_sorted_by_date_less_recent_" : "_sorted_by_date_more_recent_" + case "size": + layoutForView.titleButtonHeader = layoutForView.ascending ? "_sorted_by_size_smallest_" : "_sorted_by_size_largest_" + default: + break + } + + self.sortButton?.setTitle(NSLocalizedString(layoutForView.titleButtonHeader, comment: ""), for: .normal) + NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) + } +} diff --git a/iOSClient/Offline/NCOffline.swift b/iOSClient/Offline/NCOffline.swift index 328984af6a..08a73ae26a 100644 --- a/iOSClient/Offline/NCOffline.swift +++ b/iOSClient/Offline/NCOffline.swift @@ -35,6 +35,7 @@ class NCOffline: NCCollectionViewCommon { enableSearchBar = false headerRichWorkspaceDisable = true emptyImageName = "icloud.and.arrow.down" + emptyImage = UIImage(named: "folder") emptyTitle = "_files_no_files_" emptyDescription = "_tutorial_offline_view_" emptyDataPortaitOffset = 30 From 23af78fa306bcde0fc9a1ed0e0c9130b51bf757a Mon Sep 17 00:00:00 2001 From: TSI-amrutwaghmare <96108296+TSI-amrutwaghmare@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:09:33 +0530 Subject: [PATCH 2/3] NMC 2172 - dashboard theming customisation --- ...nViewCommon+CollectionViewDataSource.swift | 17 +++++++++++++ .../NCCollectionViewCommon.swift | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index bef043ea8b..85309dcead 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -446,6 +446,23 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.fileSharedLabel?.textColor = NCBrandColor.shared.notificationAction } + // Button More + if metadata.isInTransfer || metadata.isWaitingTransfer { + cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop) + } else if metadata.lock == true { + cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCImageCache.images.buttonMoreLock) + a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) + } else { + cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) + cell.fileSharedLabel?.text = "" + } + + if metadata.permissions.contains("S"), (metadata.permissions.range(of: "S") != nil) { + cell.fileSharedImage?.image = NCImageCache.images.sharedWithMe + cell.fileSharedLabel?.text = NSLocalizedString("_recieved_", comment: "") + cell.fileSharedLabel?.textColor = NCBrandColor.shared.notificationAction + } + // Button More if metadata.lock == true { cell.setButtonMore(image: imageCache.getImageButtonMoreLock()) diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index e3d8056199..a02961b8b3 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -1091,6 +1091,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) } + func tapButtonSwitch(_ sender: Any) { + guard !isTransitioning else { return } + isTransitioning = true + + guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return } + + if layoutForView.layout == NCGlobal.shared.layoutGrid { + layoutForView.layout = NCGlobal.shared.layoutList + } else { + layoutForView.layout = NCGlobal.shared.layoutGrid + } + self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView) + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false } + } + + func tapButtonOrder(_ sender: Any) { + let sortMenu = NCSortMenu() + sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) + } + func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -1286,6 +1308,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS if headerMenuButtonsView { size += NCGlobal.shared.heightButtonsView } + if headerMenuButtonsView { + size += NCGlobal.shared.heightButtonsView + } return size } From 8d16665794dca3e03c4392e9227226d610566f66 Mon Sep 17 00:00:00 2001 From: harshada-15-tsys Date: Mon, 14 Apr 2025 16:33:57 +0530 Subject: [PATCH 3/3] NMC 2172 - Dashboard theming customisation changes --- Nextcloud.xcodeproj/project.pbxproj | 11 + .../Cell/NCCellProtocol.swift | 20 +- .../Collection Common/Cell/NCListCell.swift | 76 ++- ...nViewCommon+CollectionViewDataSource.swift | 477 ++++++-------- .../NCCollectionViewCommon+SelectTabBar.swift | 154 +++-- .../NCCollectionViewCommon.swift | 611 +++++++----------- .../NCSectionFirstHeader.swift | 48 +- .../NCSectionFirstHeader.xib | 4 + iOSClient/Main/NCActionCenter.swift | 151 +++-- iOSClient/Menu/NCMenuAction.swift | 162 ++++- iOSClient/Menu/NCSortMenu.swift | 22 +- iOSClient/Offline/NCOffline.swift | 4 +- 12 files changed, 872 insertions(+), 868 deletions(-) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 05654d1b07..71213a66d6 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -92,6 +92,10 @@ AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; }; AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; }; AFCE353927E5DE0500FEA6C2 /* Shareable.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* Shareable.swift */; }; + AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; }; + B5C9801E2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */; }; + B5C980202DAD201A0041B146 /* NCSortMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */; }; + C04E2F232A17BB4D001BAD85 /* FilesIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04E2F222A17BB4D001BAD85 /* FilesIntegrationTests.swift */; }; D575039F27146F93008DC9DC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; }; F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; }; @@ -1330,6 +1334,9 @@ AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = ""; }; AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = ""; }; AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = ""; }; + AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = ""; }; + B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift"; sourceTree = ""; }; + B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSortMenu.swift; sourceTree = ""; }; C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; }; @@ -2076,6 +2083,7 @@ 371B5A2F23D0B04B00FAFAE9 /* Menu */ = { isa = PBXGroup; children = ( + B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */, 3704EB2923D5A58400455C5B /* NCMenu.storyboard */, 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */, AF68326927BE65A90010BF0B /* NCMenuAction.swift */, @@ -2539,6 +2547,7 @@ F7603298252F0E550015A421 /* Collection Common */ = { isa = PBXGroup; children = ( + B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */, F75FE06B2BB01D0D00A0EFEF /* Cell */, F78ACD50219046AC0088454D /* Section Header Footer */, F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */, @@ -4635,6 +4644,7 @@ F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */, F710D2022405826100A6033D /* NCViewer+Menu.swift in Sources */, F765E9CD295C585800A09ED8 /* NCUploadScanDocument.swift in Sources */, + B5C980202DAD201A0041B146 /* NCSortMenu.swift in Sources */, F741C2242B6B9FD600E849BB /* NCMediaSelectTabBar.swift in Sources */, F77A697D250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift in Sources */, F7BF9D822934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */, @@ -4667,6 +4677,7 @@ F7D4BF402CA2E8D800A5E746 /* TOPasscodeButtonLabel.m in Sources */, F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */, F7D4BF422CA2E8D800A5E746 /* TOPasscodeSettingsViewController.m in Sources */, + B5C9801E2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */, F7D4BF432CA2E8D800A5E746 /* TOPasscodeCircleImage.m in Sources */, F7D4BF442CA2E8D800A5E746 /* TOPasscodeView.m in Sources */, F7D4BF452CA2E8D800A5E746 /* TOPasscodeCircleButton.m in Sources */, diff --git a/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift b/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift index 9e969faed9..e1f97b5193 100644 --- a/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift +++ b/iOSClient/Main/Collection Common/Cell/NCCellProtocol.swift @@ -39,13 +39,13 @@ protocol NCCellProtocol { var fileSharedImage: UIImageView? { get set } var fileMoreImage: UIImageView? { get set } var cellSeparatorView: UIView? { get set } - var indexPath: IndexPath { get set } var fileSharedLabel: UILabel? { get set } + var fileProgressView: UIProgressView? { get set } func titleInfoTrailingDefault() func titleInfoTrailingFull() func writeInfoDateSize(date: NSDate, size: Int64) - func setButtonMore(image: UIImage) + func setButtonMore(named: String, image: UIImage) func hideImageItem(_ status: Bool) func hideImageFavorite(_ status: Bool) func hideImageStatus(_ status: Bool) @@ -55,6 +55,8 @@ protocol NCCellProtocol { func hideLabelSubinfo(_ status: Bool) func hideButtonShare(_ status: Bool) func hideButtonMore(_ status: Bool) + func selectMode(_ status: Bool) + func selected(_ status: Bool) func selected(_ status: Bool, isEditMode: Bool) func setAccessibility(label: String, value: String) func setTags(tags: [String]) @@ -121,11 +123,19 @@ extension NCCellProtocol { get { return nil } set { } } - + var fileProgressView: UIProgressView? { + get { return nil } + set {} + } + var fileSelectImage: UIImageView? { + get { return nil } + set {} + } + func titleInfoTrailingDefault() {} func titleInfoTrailingFull() {} func writeInfoDateSize(date: NSDate, size: Int64) {} - func setButtonMore(image: UIImage) {} + func setButtonMore(named: String, image: UIImage) {} func hideImageItem(_ status: Bool) {} func hideImageFavorite(_ status: Bool) {} func hideImageStatus(_ status: Bool) {} @@ -135,6 +145,8 @@ extension NCCellProtocol { func hideLabelSubinfo(_ status: Bool) {} func hideButtonShare(_ status: Bool) {} func hideButtonMore(_ status: Bool) {} + func selectMode(_ status: Bool) {} + func selected(_ status: Bool) {} func selected(_ status: Bool, isEditMode: Bool) {} func setAccessibility(label: String, value: String) {} func setTags(tags: [String]) {} diff --git a/iOSClient/Main/Collection Common/Cell/NCListCell.swift b/iOSClient/Main/Collection Common/Cell/NCListCell.swift index 8066919b13..0219dccc65 100755 --- a/iOSClient/Main/Collection Common/Cell/NCListCell.swift +++ b/iOSClient/Main/Collection Common/Cell/NCListCell.swift @@ -44,9 +44,9 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! @IBOutlet weak var subInfoTrailingConstraint: NSLayoutConstraint! - private var objectId = "" + private var ocId = "" + private var ocIdTransfer = "" private var user = "" - var indexPath = IndexPath() weak var listCellDelegate: NCListCellDelegate? var namedButtonMore = "" @@ -54,9 +54,9 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto var fileAvatarImageView: UIImageView? { return imageShared } - var fileObjectId: String? { - get { return objectId } - set { objectId = newValue ?? "" } + var fileOcId: String? { + get { return ocId } + set { ocId = newValue ?? "" } } var filePreviewImageView: UIImageView? { get { return imageItem } @@ -118,7 +118,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto override func awakeFromNib() { super.awakeFromNib() + initCell() + } + func initCell() { + imageItem.layer.cornerRadius = 6 imageItem.layer.masksToBounds = true @@ -128,9 +132,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto accessibilityValue = nil isAccessibilityElement = true - progressView.tintColor = NCBrandColor.shared.brandElement + progressView.tintColor = NCBrandColor.shared.brand progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5) progressView.trackTintColor = .clear + imageSelect.isHidden = true let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:))) longPressedGesture.minimumPressDuration = 0.5 @@ -140,20 +145,21 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto separator.backgroundColor = .separator separatorHeightConstraint.constant = 0.5 + titleInfoTrailingDefault() labelTitle.text = "" labelInfo.text = "" labelTitle.textColor = .label labelInfo.textColor = .systemGray labelSubinfo.textColor = .systemGray + setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore) + imageMore.isHidden = false + buttonMore.isHidden = false } override func prepareForReuse() { super.prepareForReuse() - imageItem.backgroundColor = nil - accessibilityHint = nil - accessibilityLabel = nil - accessibilityValue = nil + initCell() } override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? { @@ -161,15 +167,19 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } @IBAction func touchUpInsideShare(_ sender: Any) { - listCellDelegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender) + listCellDelegate?.tapShareListItem(with: ocId, ocIdTransfer: ocIdTransfer, sender: sender) } @IBAction func touchUpInsideMore(_ sender: Any) { - listCellDelegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) + listCellDelegate?.tapMoreListItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: imageItem.image, sender: sender) } @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - listCellDelegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) + listCellDelegate?.longPressListItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, gestureRecognizer: gestureRecognizer) + } + + @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) { + listCellDelegate?.longPressMoreListItem(with: ocId, namedButtonMore: namedButtonMore, gestureRecognizer: gestureRecognizer) } fileprivate func setA11yActions() { @@ -215,11 +225,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto separator.isHidden = status } - func selected(_ status: Bool, isEditMode: Bool) { - if isEditMode { + func selectMode(_ status: Bool) { + if status { imageItemLeftConstraint.constant = 45 imageSelect.isHidden = false - imageShared.isHidden = true imageMore.isHidden = true buttonShared.isHidden = true buttonMore.isHidden = true @@ -227,28 +236,46 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } else { imageItemLeftConstraint.constant = 10 imageSelect.isHidden = true - imageShared.isHidden = false imageMore.isHidden = false buttonShared.isHidden = false buttonMore.isHidden = false backgroundView = nil setA11yActions() } + } + + func selected(_ status: Bool, isEditMode: Bool) { + // NMC-1190 - iOS - Files - Deleting files while files are still uploading won't delete properly : to fix this issue remove check for !metadata.isInTransfer in below line + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), !metadata.isInTransfer, !metadata.e2eEncrypted else { + backgroundView = nil + separator.isHidden = false + imageSelect.isHidden = true + + return + } + if status { + var blurEffect: UIVisualEffect? var blurEffectView: UIView? - blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial)) - blurEffectView?.backgroundColor = .lightGray + if traitCollection.userInterfaceStyle == .dark { + blurEffect = UIBlurEffect(style: .dark) + blurEffectView = UIVisualEffectView(effect: blurEffect) + blurEffectView?.backgroundColor = .black + } else { + blurEffect = UIBlurEffect(style: .extraLight) + blurEffectView = UIVisualEffectView(effect: blurEffect) + blurEffectView?.backgroundColor = .lightGray + } blurEffectView?.frame = self.bounds blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] - imageSelect.image = NCImageCache.images.checkedYes backgroundView = blurEffectView + imageSelect.image = NCImageCache.images.checkedYes separator.isHidden = true } else { imageSelect.image = NCImageCache.images.checkedNo backgroundView = nil separator.isHidden = false } - } func writeInfoDateSize(date: NSDate, size: Int64) { @@ -263,9 +290,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } protocol NCListCellDelegate: AnyObject { - func tapShareListItem(with objectId: String, indexPath: IndexPath, sender: Any) - func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, indexPath: IndexPath, sender: Any) - func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) + func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) + func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) + func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) + func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) } // MARK: - List Layout diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index 85309dcead..03c6d1db3f 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -32,97 +32,20 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + let numberItems = dataSource.numberOfItemsInSection(section) + emptyDataSet?.numberOfItemsInSection(numberItems, section: section) // get auto upload folder self.autoUploadFileName = self.database.getAccountAutoUploadFileName() self.autoUploadDirectory = self.database.getAccountAutoUploadDirectory(session: self.session) // get layout for view self.layoutForView = self.database.getLayoutForView(account: self.session.account, key: self.layoutKey, serverUrl: self.serverUrl) - return self.dataSource.numberOfItemsInSection(section) - } - - func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - guard let metadata = dataSource.cellForItemAt(indexPath: indexPath), - let cell = (cell as? NCCellProtocol) else { return } - let existsIcon = utilityFileSystem.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) - - func downloadAvatar(fileName: String, user: String, dispalyName: String?) { - if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) { - cell.fileAvatarImageView?.contentMode = .scaleAspectFill - cell.fileAvatarImageView?.image = image - } else { - NCNetworking.shared.downloadAvatar(user: user, dispalyName: dispalyName, fileName: fileName, cell: cell, view: collectionView) - } - } - /// CONTENT MODE - cell.filePreviewImageView?.layer.borderWidth = 0 - if existsIcon { - cell.filePreviewImageView?.contentMode = .scaleAspectFill - } else { - cell.filePreviewImageView?.contentMode = .scaleAspectFit - } - cell.fileAvatarImageView?.contentMode = .center - /// THUMBNAIL - if !metadata.directory { - if metadata.hasPreviewBorder { - cell.filePreviewImageView?.layer.borderWidth = 0.2 - cell.filePreviewImageView?.layer.borderColor = UIColor.lightGray.cgColor - } - if metadata.name == NCGlobal.shared.appName { - if layoutForView?.layout == NCGlobal.shared.layoutPhotoRatio || layoutForView?.layout == NCGlobal.shared.layoutPhotoSquare { - if let image = NCImageCache.shared.getPreviewImageCache(ocId: metadata.ocId, etag: metadata.etag) { - cell.filePreviewImageView?.image = image - } else if let image = UIImage(contentsOfFile: self.utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)) { - cell.filePreviewImageView?.image = image - NCImageCache.shared.addPreviewImageCache(metadata: metadata, image: image) - } - } else { - if let image = NCImageCache.shared.getIconImageCache(ocId: metadata.ocId, etag: metadata.etag) { - cell.filePreviewImageView?.image = image - } else if metadata.hasPreview { - cell.filePreviewImageView?.image = utility.getIcon(metadata: metadata) - } - } - if cell.filePreviewImageView?.image == nil { - if metadata.iconName.isEmpty { - cell.filePreviewImageView?.image = NCImageCache.images.file - } else { - cell.filePreviewImageView?.image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true) - } - if metadata.hasPreview && metadata.status == NCGlobal.shared.metadataStatusNormal && !existsIcon { - for case let operation as NCCollectionViewDownloadThumbnail in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { return } - NCNetworking.shared.downloadThumbnailQueue.addOperation(NCCollectionViewDownloadThumbnail(metadata: metadata, cell: cell, collectionView: collectionView)) - } - } - } else { - /// APP NAME - UNIFIED SEARCH - switch metadata.iconName { - case let str where str.contains("contacts"): - cell.filePreviewImageView?.image = NCImageCache.images.iconContacts - case let str where str.contains("conversation"): - cell.filePreviewImageView?.image = NCImageCache.images.iconTalk - case let str where str.contains("calendar"): - cell.filePreviewImageView?.image = NCImageCache.images.iconCalendar - case let str where str.contains("deck"): - cell.filePreviewImageView?.image = NCImageCache.images.iconDeck - case let str where str.contains("mail"): - cell.filePreviewImageView?.image = NCImageCache.images.iconMail - case let str where str.contains("talk"): - cell.filePreviewImageView?.image = NCImageCache.images.iconTalk - case let str where str.contains("confirm"): - cell.filePreviewImageView?.image = NCImageCache.images.iconConfirm - case let str where str.contains("pages"): - cell.filePreviewImageView?.image = NCImageCache.images.iconPages - default: - cell.filePreviewImageView?.image = NCImageCache.images.iconFile - } - } - } + return numberItems } func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if !collectionView.indexPathsForVisibleItems.contains(indexPath) { - guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return } + guard let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return } for case let operation as NCCollectionViewDownloadThumbnail in NCNetworking.shared.downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { operation.cancel() } @@ -130,7 +53,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - guard let metadata = dataSource.getMetadata(indexPath: indexPath) else { return } + guard let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return } let existsImagePreview = utilityFileSystem.fileProviderStorageImageExists(metadata.ocId, etag: metadata.etag) let ext = global.getSizeExtension(column: self.numberOfColumns) @@ -193,8 +116,10 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { /// Edit mode if fileSelect.contains(metadata.ocId) { + cell.selectMode(true) cell.selected(true, isEditMode: isEditMode) } else { + cell.selectMode(false) cell.selected(false, isEditMode: isEditMode) } @@ -229,8 +154,10 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { listCell.listCellDelegate = self cell = listCell } - let metadata = self.dataSource.getMetadata(indexPath: indexPath) ?? tableMetadata() + guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return cell } let shares = NCManageDatabase.shared.getTableShares(metadata: metadata) + let existsImagePreview = utilityFileSystem.fileProviderStorageImageExists(metadata.ocId, etag: metadata.etag) + let ext = global.getSizeExtension(column: self.numberOfColumns) defer { if !metadata.isSharable() || NCCapabilities.shared.disableSharesView(account: metadata.account) { @@ -245,30 +172,6 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { utility.createImageFileFrom(metadata: metadata) } - // LAYOUT PHOTO - if isLayoutPhoto { - if metadata.isImageOrVideo { - let photoCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? NCPhotoCell)! - photoCell.photoCellDelegate = self - cell = photoCell - return self.photoCell(cell: photoCell, indexPath: indexPath, metadata: metadata, ext: ext) - } else { - let gridCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell)! - gridCell.gridCellDelegate = self - cell = gridCell - } - } else if isLayoutGrid { - // LAYOUT GRID - let gridCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell)! - gridCell.gridCellDelegate = self - cell = gridCell - } else { - // LAYOUT LIST - let listCell = (collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell)! - listCell.listCellDelegate = self - cell = listCell - } - /// CONTENT MODE cell.fileAvatarImageView?.contentMode = .center cell.filePreviewImageView?.layer.borderWidth = 0 @@ -279,8 +182,6 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.filePreviewImageView?.contentMode = .scaleAspectFit } - guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return cell } - if metadataFolder != nil { isShare = metadata.permissions.contains(permissions.permissionShared) && !metadataFolder!.permissions.contains(permissions.permissionShared) isMounted = metadata.permissions.contains(permissions.permissionMounted) && !metadataFolder!.permissions.contains(permissions.permissionMounted) @@ -290,19 +191,37 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.fileOcId = metadata.ocId cell.fileOcIdTransfer = metadata.ocIdTransfer cell.fileUser = metadata.ownerId - + cell.fileSelectImage?.image = nil + cell.fileStatusImage?.image = nil + cell.fileLocalImage?.image = nil + cell.fileFavoriteImage?.image = nil + cell.fileMoreImage?.image = nil + cell.filePreviewImageView?.image = nil + cell.filePreviewImageView?.backgroundColor = nil + cell.fileProgressView?.isHidden = true + cell.fileProgressView?.progress = 0.0 + cell.hideButtonShare(false) + cell.hideButtonMore(false) + cell.titleInfoTrailingDefault() + if isSearchingMode { cell.fileTitleLabel?.text = metadata.fileName cell.fileTitleLabel?.lineBreakMode = .byTruncatingTail if metadata.name == global.appName { - cell.fileInfoLabel?.text = NSLocalizedString("_in_", comment: "") + " " + utilityFileSystem.getPath(path: metadata.path, user: metadata.user) + cell.fileInfoLabel?.text = utility.dateDiff(metadata.date as Date) + " · " + utilityFileSystem.transformedSize(metadata.size) } else { cell.fileInfoLabel?.text = metadata.subline } cell.fileSubinfoLabel?.isHidden = true } else if !metadata.sessionError.isEmpty, metadata.status != global.metadataStatusNormal { - cell.fileSubinfoLabel?.isHidden = false - cell.fileInfoLabel?.text = metadata.sessionError + // Temporary issue fix for NMC-3771: iOS v9.1.6 > multiple uploads cause error messages + if metadata.sessionError == "423: WebDAV Locked: Trying to access locked resource" || metadata.sessionError == "423: WebDAV gesperrt: Zugriffsversuch auf eine gesperrte Ressource" { + cell.fileTitleLabel?.text = metadata.fileName + cell.fileTitleLabel?.lineBreakMode = .byTruncatingMiddle + } else { + cell.fileSubinfoLabel?.isHidden = false + cell.fileInfoLabel?.text = metadata.sessionError + } } else { cell.fileSubinfoLabel?.isHidden = false cell.fileTitleLabel?.text = metadata.fileNameView @@ -310,6 +229,10 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.writeInfoDateSize(date: metadata.date, size: metadata.size) } + if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { + cell.fileProgressView?.isHidden = false + } + // Accessibility [shared] if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { if metadata.ownerId != metadata.userId { a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) @@ -318,23 +241,23 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { if metadata.directory { let tableDirectory = database.getTableDirectory(ocId: metadata.ocId) if metadata.e2eEncrypted { - cell.filePreviewImageView?.image = imageCache.getFolderEncrypted(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderEncrypted() } else if isShare { - cell.filePreviewImageView?.image = imageCache.getFolderSharedWithMe(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderSharedWithMe() } else if !metadata.shareType.isEmpty { metadata.shareType.contains(3) ? - (cell.filePreviewImageView?.image = imageCache.getFolderPublic(account: metadata.account)) : - (cell.filePreviewImageView?.image = imageCache.getFolderSharedWithMe(account: metadata.account)) + (cell.filePreviewImageView?.image = imageCache.getFolderPublic()) : + (cell.filePreviewImageView?.image = imageCache.getFolderSharedWithMe()) } else if !metadata.shareType.isEmpty && metadata.shareType.contains(3) { - cell.filePreviewImageView?.image = imageCache.getFolderPublic(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderPublic() } else if metadata.mountType == "group" { - cell.filePreviewImageView?.image = imageCache.getFolderGroup(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderGroup() } else if isMounted { - cell.filePreviewImageView?.image = imageCache.getFolderExternal(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderExternal() } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory { - cell.filePreviewImageView?.image = imageCache.getFolderAutomaticUpload(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolderAutomaticUpload() } else { - cell.filePreviewImageView?.image = imageCache.getFolder(account: metadata.account) + cell.filePreviewImageView?.image = imageCache.getFolder() } // Local image: offline @@ -355,6 +278,8 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { if metadata.name == global.appName { if let image = NCImageCache.shared.getImageCache(ocId: metadata.ocId, etag: metadata.etag, ext: ext) { cell.filePreviewImageView?.image = image + } else if metadata.fileExtension == "odg" { + cell.filePreviewImageView?.image = UIImage(named: "diagram") } else if let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: ext) { cell.filePreviewImageView?.image = image } @@ -370,23 +295,23 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { /// APP NAME - UNIFIED SEARCH switch metadata.iconName { case let str where str.contains("contacts"): - cell.filePreviewImageView?.image = utility.loadImage(named: "person.crop.rectangle.stack", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconContacts case let str where str.contains("conversation"): - cell.filePreviewImageView?.image = UIImage(named: "talk-template")!.image(color: NCBrandColor.shared.getElement(account: metadata.account)) + cell.filePreviewImageView?.image = NCImageCache.images.iconTalk case let str where str.contains("calendar"): - cell.filePreviewImageView?.image = utility.loadImage(named: "calendar", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconCalendar case let str where str.contains("deck"): - cell.filePreviewImageView?.image = utility.loadImage(named: "square.stack.fill", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconDeck case let str where str.contains("mail"): - cell.filePreviewImageView?.image = utility.loadImage(named: "mail", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconMail case let str where str.contains("talk"): - cell.filePreviewImageView?.image = UIImage(named: "talk-template")!.image(color: NCBrandColor.shared.getElement(account: metadata.account)) + cell.filePreviewImageView?.image = NCImageCache.images.iconTalk case let str where str.contains("confirm"): - cell.filePreviewImageView?.image = utility.loadImage(named: "arrow.right", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconConfirm case let str where str.contains("pages"): - cell.filePreviewImageView?.image = utility.loadImage(named: "doc.richtext", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.iconPages default: - cell.filePreviewImageView?.image = utility.loadImage(named: "doc", colors: [NCBrandColor.shared.iconImageColor]) + cell.filePreviewImageView?.image = NCImageCache.images.file } if !metadata.iconUrl.isEmpty { if let ownerId = getAvatarFromIconUrl(metadata: metadata) { @@ -425,7 +350,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { if isShare || !metadata.shareType.isEmpty { cell.fileSharedImage?.image = NCImageCache.images.shared } else { - cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) + cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60) cell.fileSharedLabel?.text = "" } if appDelegate.account != metadata.account { @@ -436,7 +361,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { if (!metadata.shareType.isEmpty || !(shares.share?.isEmpty ?? true) || (shares.firstShareLink != nil)){ cell.fileSharedImage?.image = cell.fileSharedImage?.image?.imageColor(NCBrandColor.shared.customer) } else { - cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) + cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60) cell.fileSharedLabel?.text = "" } @@ -453,27 +378,12 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCImageCache.images.buttonMoreLock) a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) } else { - cell.fileSharedImage?.image = NCImageCache.images.canShare.image(color: NCBrandColor.shared.gray60, size: 50) - cell.fileSharedLabel?.text = "" - } - - if metadata.permissions.contains("S"), (metadata.permissions.range(of: "S") != nil) { - cell.fileSharedImage?.image = NCImageCache.images.sharedWithMe - cell.fileSharedLabel?.text = NSLocalizedString("_recieved_", comment: "") - cell.fileSharedLabel?.textColor = NCBrandColor.shared.notificationAction - } - - // Button More - if metadata.lock == true { - cell.setButtonMore(image: imageCache.getImageButtonMoreLock()) - a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) - } else { - cell.setButtonMore(image: imageCache.getImageButtonMore()) + cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore) } // Staus if metadata.isLivePhoto { - cell.fileStatusImage?.image = utility.loadImage(named: "livephoto", colors: isLayoutPhoto ? [.white] : [NCBrandColor.shared.iconImageColor2]) + cell.fileStatusImage?.image = NCImageCache.shared.getImageLivePhoto() a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) } else if metadata.isVideo { cell.fileStatusImage?.image = utility.loadImage(named: "play.circle", colors: NCBrandColor.shared.iconImageMultiColors) @@ -506,25 +416,6 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { break } - // AVATAR - if !metadata.ownerId.isEmpty, metadata.ownerId != metadata.userId { - cell.fileAvatarImageView?.contentMode = .scaleAspectFill - - let fileName = NCSession.shared.getFileName(urlBase: metadata.urlBase, user: metadata.ownerId) - let results = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) - - if results.image == nil { - cell.fileAvatarImageView?.image = utility.loadUserImage(for: metadata.ownerId, displayName: metadata.ownerDisplayName, urlBase: metadata.urlBase) - } else { - cell.fileAvatarImageView?.image = results.image - } - - if !(results.tableAvatar?.loaded ?? false), - NCNetworking.shared.downloadAvatarQueue.operations.filter({ ($0 as? NCOperationDownloadAvatar)?.fileName == fileName }).isEmpty { - NCNetworking.shared.downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: metadata.ownerId, fileName: fileName, account: metadata.account, view: collectionView)) - } - } - // URL if metadata.classFile == NKCommon.TypeClassFile.url.rawValue { cell.fileLocalImage?.image = nil @@ -543,11 +434,16 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } // Edit mode - if fileSelect.contains(metadata.ocId) { - cell.selected(true, isEditMode: isEditMode) - a11yValues.append(NSLocalizedString("_selected_", comment: "")) + if isEditMode { + cell.selectMode(true) + if fileSelect.contains(metadata.ocId) { + cell.selected(true, isEditMode: isEditMode) + a11yValues.append(NSLocalizedString("_selected_", comment: "")) + } else { + cell.selected(false, isEditMode: isEditMode) + } } else { - cell.selected(false, isEditMode: isEditMode) + cell.selectMode(false) } // Accessibility @@ -567,33 +463,6 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // TAGS cell.setTags(tags: Array(metadata.tags)) - // Layout photo - if isLayoutPhoto { - let width = UIScreen.main.bounds.width / CGFloat(self.numberOfColumns) - - cell.hideImageFavorite(false) - cell.hideImageLocal(false) - cell.hideImageItem(false) - cell.hideButtonMore(false) - cell.hideLabelInfo(false) - cell.hideLabelSubinfo(false) - cell.hideImageStatus(false) - cell.fileTitleLabel?.font = UIFont.systemFont(ofSize: 15) - - if width < 120 { - cell.hideImageFavorite(true) - cell.hideImageLocal(true) - cell.fileTitleLabel?.font = UIFont.systemFont(ofSize: 10) - if width < 100 { - cell.hideImageItem(true) - cell.hideButtonMore(true) - cell.hideLabelInfo(true) - cell.hideLabelSubinfo(true) - cell.hideImageStatus(true) - } - } - } - // Hide buttons if metadata.name != global.appName { cell.titleInfoTrailingFull() @@ -616,101 +485,15 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - func setContent(header: UICollectionReusableView, indexPath: IndexPath) { - let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: indexPath.section) if kind == UICollectionView.elementKindSectionHeader { - if dataSource.getMetadataSourceForAllSections().isEmpty { - - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - self.sectionFirstHeaderEmptyData = header - header.delegate = self - - } - if let header = header as? NCSectionFirstHeader { - let recommendations = self.database.getRecommendedFiles(account: self.session.account) - var sectionText = NSLocalizedString("_all_files_", comment: "") - - if NCKeychain().getPersonalFilesOnly(account: session.account) { - sectionText = NSLocalizedString("_personal_files_", comment: "") - } - - if !self.dataSource.getSectionValueLocalization(indexPath: indexPath).isEmpty { - sectionText = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - } - - header.setContent(heightHeaderRichWorkspace: heightHeaderRichWorkspace, - richWorkspaceText: richWorkspaceText, - heightHeaderRecommendations: heightHeaderRecommendations, - recommendations: recommendations, - heightHeaderSection: heightHeaderSection, - sectionText: sectionText, - viewController: self, - delegate: self) - - } else if let header = header as? NCSectionFirstHeaderEmptyData { - var emptyImage: UIImage? - var emptyTitle: String? - - if isSearchingMode { - emptyImage = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.getElement(account: session.account)]) - if self.dataSourceTask?.state == .running { - emptyTitle = NSLocalizedString("_search_in_progress_", comment: "") - } else { - emptyTitle = NSLocalizedString("_search_no_record_found_", comment: "") - } - emptyDescription = NSLocalizedString("_search_instruction_", comment: "") - } else if self.dataSourceTask?.state == .running { - emptyImage = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)]) - emptyTitle = NSLocalizedString("_request_in_progress_", comment: "") - emptyDescription = "" - } else { - if serverUrl.isEmpty { - if let emptyImageName { - emptyImage = utility.loadImage(named: emptyImageName, colors: emptyImageColors != nil ? emptyImageColors : [NCBrandColor.shared.getElement(account: session.account)]) - } else { - emptyImage = imageCache.getFolder(account: session.account) - } - emptyTitle = NSLocalizedString(self.emptyTitle, comment: "") - emptyDescription = NSLocalizedString(emptyDescription, comment: "") - } else if metadataFolder?.status == global.metadataStatusWaitCreateFolder { - emptyImage = utility.loadImage(named: "arrow.triangle.2.circlepath", colors: [NCBrandColor.shared.getElement(account: session.account)]) - emptyTitle = NSLocalizedString("_files_no_files_", comment: "") - emptyDescription = NSLocalizedString("_folder_offline_desc_", comment: "") - } else { - emptyImage = imageCache.getFolder(account: session.account) - emptyTitle = NSLocalizedString("_files_no_files_", comment: "") - emptyDescription = NSLocalizedString("_no_file_pull_down_", comment: "") - } - } - - header.setContent(emptyImage: emptyImage, - emptyTitle: emptyTitle, - emptyDescription: emptyDescription, - delegate: self) - - } else if let header = header as? NCSectionHeader { - let text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - - header.setContent(text: text) - } - } - - if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader { - if self.dataSource.isEmpty() { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - - self.sectionFirstHeaderEmptyData = header - setContent(header: header, indexPath: indexPath) - - return header + if indexPath.section == 0 { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCSectionHeaderMenu else { return UICollectionReusableView() } + let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section) - } else if indexPath.section == 0 { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } - - self.sectionFirstHeader = header -// setContent(header: header, indexPath: indexPath) + self.headerMenu = header + self.headerMenu?.setViewTransfer(isHidden: true) if layoutForView?.layout == NCGlobal.shared.layoutGrid { header.setImageSwitchList() header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") @@ -718,16 +501,19 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { header.setImageSwitchGrid() header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") } - header.delegate = self - if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false) + header.delegate = self + + if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil, let ocId = NCNetworking.shared.transferInForegorund?.ocId { + let text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand) +// header.setViewTransfer(isHidden: false, text: text) + header.setViewTransfer(isHidden: false, ocId: ocId, text: text, progress: NCNetworking.shared.transferInForegorund?.progress) } else { header.setViewTransfer(isHidden: true) } if headerMenuButtonsView { - header.setStatusButtonsView(enable: !dataSource.getMetadataSourceForAllSections().isEmpty) + header.setStatusButtonsView(enable: !dataSource.isEmpty()) header.setButtonsView(height: NCGlobal.shared.heightButtonsView) header.setSortedTitle(layoutForView?.titleButtonHeader ?? "") } else { @@ -768,6 +554,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { footer.setTitleLabel("") footer.setButtonText(NSLocalizedString("_show_more_results_", comment: "")) + footer.buttonSection.setTitleColor(NCBrandColor.shared.customer, for: .normal) footer.separatorIsHidden(true) footer.buttonIsHidden(true) footer.hideActivityIndicatorSection() @@ -801,6 +588,14 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } } + func setContent(header: UICollectionReusableView, indexPath: IndexPath) { + if let header = header as? NCSectionHeader { + let text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) + + header.setContent(text: text) + } + } + // MARK: - func getAvatarFromIconUrl(metadata: tableMetadata) -> String? { @@ -819,4 +614,96 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } return ownerId } + + // MARK: - Cancel (Download Upload) + + // sessionIdentifierDownload: String = "com.nextcloud.nextcloudkit.session.download" + // sessionIdentifierUpload: String = "com.nextcloud.nextcloudkit.session.upload" + + // sessionUploadBackground: String = "com.nextcloud.session.upload.background" + // sessionUploadBackgroundWWan: String = "com.nextcloud.session.upload.backgroundWWan" + // sessionUploadBackgroundExtension: String = "com.nextcloud.session.upload.extension" + + func cancelSession(metadata: tableMetadata) async { + + let fileNameLocalPath = utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) + utilityFileSystem.removeFile(atPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId)) + NCManageDatabase.shared.deleteLocalFileOcId(metadata.ocId) + + // No session found + if metadata.session.isEmpty { + NCNetworking.shared.uploadRequest.removeValue(forKey: fileNameLocalPath) + NCNetworking.shared.downloadRequest.removeValue(forKey: fileNameLocalPath) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) + return + } + + // DOWNLOAD FOREGROUND + if metadata.session == NextcloudKit.shared.nkCommonInstance.identifierSessionDownload { + if let request = NCNetworking.shared.downloadRequest[fileNameLocalPath] { + request.cancel() + } else if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) { + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, + session: "", + sessionError: "", + selector: "", + status: NCGlobal.shared.metadataStatusNormal) + NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadCancelFile), + object: nil, + userInfo: ["ocId": metadata.ocId, + "serverUrl": metadata.serverUrl, + "account": metadata.account]) + } + return + } + + // DOWNLOAD BACKGROUND + if metadata.session == NCNetworking.shared.sessionDownloadBackground { + let session: URLSession? = NCNetworking.shared.sessionManagerDownloadBackground + if let tasks = await session?.tasks { + for task in tasks.2 { // ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) + if task.taskIdentifier == metadata.sessionTaskIdentifier { + task.cancel() + } + } + } + } + + // UPLOAD FOREGROUND + if metadata.session == NextcloudKit.shared.nkCommonInstance.identifierSessionUpload { + if let request = NCNetworking.shared.uploadRequest[fileNameLocalPath] { + request.cancel() + NCNetworking.shared.uploadRequest.removeValue(forKey: fileNameLocalPath) + } + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterUploadCancelFile), + object: nil, + userInfo: ["ocId": metadata.ocId, + "serverUrl": metadata.serverUrl, + "account": metadata.account]) + return + } + + // UPLOAD BACKGROUND + var session: URLSession? + if metadata.session == NCNetworking.shared.sessionUploadBackground { + session = NCNetworking.shared.sessionManagerUploadBackground + } else if metadata.session == NCNetworking.shared.sessionUploadBackgroundWWan { + session = NCNetworking.shared.sessionManagerUploadBackgroundWWan + } + if let tasks = await session?.tasks { + for task in tasks.1 { // ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask]) + if task.taskIdentifier == metadata.sessionTaskIdentifier { + task.cancel() + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterUploadCancelFile), + object: nil, + userInfo: ["ocId": metadata.ocId, + "serverUrl": metadata.serverUrl, + "account": metadata.account]) + } + } + } + } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift index 13c3b96dfa..617790e6b9 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift @@ -25,15 +25,15 @@ import UIKit import Foundation import NextcloudKit -extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { +extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate, NCSelectableNavigationView { + func selectAll() { - if !fileSelect.isEmpty, self.dataSource.getMetadatas().count == fileSelect.count { - fileSelect = [] - } else { - fileSelect = self.dataSource.getMetadatas().compactMap({ $0.ocId }) - } + fileSelect = selectableDataSource.compactMap({ $0.primaryKeyValue }) tabBarSelect?.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: session.userId) - self.collectionView.reloadData() + DispatchQueue.main.async { + self.collectionView.reloadData() + self.setNavigationRightItems(enableMenu: false) + } } func delete() { @@ -44,14 +44,14 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { let canDeleteServer = metadatas.allSatisfy { !$0.lock } if canDeleteServer { - alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { [self] _ in NCNetworking.shared.deleteMetadatas(metadatas, sceneIdentifier: self.controller?.sceneIdentifier) NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) - self.setEditMode(false) + toggleSelect() }) } - alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in + alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { [self] (_: UIAlertAction) in let copyMetadatas = metadatas Task { @@ -65,7 +65,7 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { } NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "error": error]) } - self.setEditMode(false) + toggleSelect() }) alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in }) @@ -76,13 +76,13 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { let metadatas = getSelectedMetadatas() NCActionCenter.shared.openSelectView(items: metadatas, controller: self.controller) - setEditMode(false) + toggleSelect() } func share() { let metadatas = getSelectedMetadatas() NCActionCenter.shared.openActivityViewController(selectedMetadata: metadatas, controller: self.controller) - setEditMode(false) + toggleSelect() } func saveAsAvailableOffline(isAnyOffline: Bool) { @@ -92,15 +92,15 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { title: NSLocalizedString("_set_available_offline_", comment: ""), message: NSLocalizedString("_select_offline_warning_", comment: ""), preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in + alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { [self] _ in metadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } - self.setEditMode(false) + toggleSelect() })) alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)) self.present(alert, animated: true) } else { metadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } - setEditMode(false) + toggleSelect() } } @@ -109,7 +109,7 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { for metadata in metadatas where metadata.lock == isAnyLocked { NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !isAnyLocked) } - setEditMode(false) + toggleSelect() } func getSelectedMetadatas() -> [tableMetadata] { @@ -133,10 +133,13 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { navigationItem.leftBarButtonItems = nil } else { ///Magentacloud branding changes hide user account button on left navigation bar -// setNavigationLeftItems() + setNavigationLeftItems() } - (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() + navigationController?.interactivePopGestureRecognizer?.isEnabled = !editMode + navigationItem.hidesBackButton = editMode + searchController(enabled: !editMode) + self.setNavigationRightItems(enableMenu: true) self.collectionView.reloadData() } @@ -156,10 +159,7 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { func toggleSelect(isOn: Bool? = nil) { DispatchQueue.main.async { self.isEditMode = isOn ?? !self.isEditMode - self.selectOcId.removeAll() - self.setNavigationLeftItems() - self.setNavigationRightItems() - self.collectionView.reloadData() + self.setEditMode(self.isEditMode) } } @@ -169,11 +169,11 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { actions.append(.cancelAction { self.toggleSelect() }) - if selectOcId.count != dataSource.getMetadataSourceForAllSections().count { + if fileSelect.count != selectableDataSource.count { actions.append(.selectAllAction(action: selectAll)) } - guard !selectOcId.isEmpty else { return actions } + guard !fileSelect.isEmpty else { return actions } actions.append(.seperator(order: 0)) @@ -186,10 +186,10 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { var canOpenIn = false var isDirectoryE2EE = false - for ocId in selectOcId { + for ocId in fileSelect { guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue } if metadata.e2eEncrypted { - selectOcId.removeAll(where: {$0 == metadata.ocId}) + fileSelect.removeAll(where: {$0 == metadata.ocId}) } else { selectedMetadatas.append(metadata) } @@ -200,14 +200,14 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { if metadata.directory { isAnyFolder = true } if metadata.lock { isAnyLocked = true - if metadata.lockOwner != appDelegate.userId { + if metadata.lockOwner != session.userId { canUnlock = false } } guard !isAnyOffline else { continue } if metadata.directory, - let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) { + let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, metadata.serverUrl + "/" + metadata.fileName)) { isAnyOffline = directory.offline } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) { isAnyOffline = localFile.offline @@ -223,43 +223,15 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { } if canOpenIn { - actions.append(.share(selectedMetadatas: selectedMetadatas, viewController: self, completion: { self.toggleSelect() })) + actions.append(.openInAction(selectedMetadatas: selectedMetadatas, controller: self.controller, completion: { self.toggleSelect() })) } - if !isAnyFolder, canUnlock, !NCGlobal.shared.capabilityFilesLockVersion.isEmpty { + if !isAnyFolder, canUnlock, !NCCapabilities.shared.getCapabilities(account: controller?.account).capabilityFilesLockVersion.isEmpty { actions.append(.lockUnlockFiles(shouldLock: !isAnyLocked, metadatas: selectedMetadatas, completion: { self.toggleSelect() })) } if !selectedMediaMetadatas.isEmpty { - var title: String = NSLocalizedString("_save_selected_files_", comment: "") - var icon = NCUtility().loadImage(named: "save_files",colors: [NCBrandColor.shared.iconImageColor]) - if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) { - title = NSLocalizedString("_livephoto_save_", comment: "") - icon = NCUtility().loadImage(named: "livephoto") - } - - actions.append(NCMenuAction( - title: title, - icon: icon, - order: 0, - action: { _ in - for metadata in selectedMediaMetadatas { - if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) { - NCNetworking.shared.saveLivePhotoQueue.addOperation(NCOperationSaveLivePhoto(metadata: metadata, metadataMOV: metadataMOV, hudView: self.view)) - } else { - if NCUtilityFileSystem().fileProviderStorageExists(metadata) { - NCActionCenter.shared.saveAlbum(metadata: metadata, controller: self.tabBarController as? NCMainTabBarController) - } else { - if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty { - NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)) - } - } - } - } - self.toggleSelect() - } - ) - ) + actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, controller: self.controller, completion: { self.toggleSelect() })) } actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: { self.reloadDataSource() @@ -274,4 +246,66 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { return actions } + func setNavigationRightItems(enableMenu: Bool = false) { + DispatchQueue.main.async { + if self.isEditMode { + let more = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain) { self.presentMenu(with: self.createMenuActions())} + self.navigationItem.rightBarButtonItems = [more] + } else { + let select = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain) { self.toggleSelect() } + let notification = UIBarButtonItem(image: UIImage(systemName: "bell"), style: .plain, action: self.tapNotification) + if self.layoutKey == NCGlobal.shared.layoutViewFiles { + self.navigationItem.rightBarButtonItems = [select, notification] + } else { + self.navigationItem.rightBarButtonItems = [select] + } + let transfer = UIBarButtonItem(image: UIImage(systemName: "arrow.left.arrow.right.circle.fill"), style: .plain, action: self.tapTransfer) + let resultsCount = self.database.getResultsMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal))?.count ?? 0 + + if self.layoutKey == NCGlobal.shared.layoutViewFiles { + self.navigationItem.rightBarButtonItems = resultsCount > 0 ? [select, notification, transfer] : [select, notification] + } else { + self.navigationItem.rightBarButtonItems = [select] + } + } + guard self.layoutKey == NCGlobal.shared.layoutViewFiles else { return } + self.navigationItem.title = self.titleCurrentFolder + } + } + + func onListSelected() { + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") + layoutForView?.layout = NCGlobal.shared.layoutList + NCManageDatabase.shared.setLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + self.groupByField = "name" + if self.dataSource.groupByField != self.groupByField { + self.dataSource.changeGroupByField(self.groupByField) + } + self.saveLayout(layoutForView!) + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false } + } + } + + func onGridSelected() { + if layoutForView?.layout == NCGlobal.shared.layoutList { + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") + layoutForView?.layout = NCGlobal.shared.layoutGrid + NCManageDatabase.shared.setLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + if isSearchingMode { + self.groupByField = "name" + } else { + self.groupByField = "classFile" + } + if self.dataSource.groupByField != self.groupByField { + self.dataSource.changeGroupByField(self.groupByField) + } + self.saveLayout(layoutForView!) + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false } + } + } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index a02961b8b3..717191ef9c 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -27,7 +27,7 @@ import RealmSwift import NextcloudKit import EasyTipView -class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionFirstHeaderDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate { +class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCPhotoCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, NCSectionFirstHeaderEmptyDataDelegate, NCAccountSettingsModelDelegate, UIAdaptivePresentationControllerDelegate, UIContextMenuInteractionDelegate, NCEmptyDataSetDelegate { @IBOutlet weak var collectionView: UICollectionView! @@ -68,21 +68,22 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var attributesZoomIn: UIMenuElement.Attributes = [] var attributesZoomOut: UIMenuElement.Attributes = [] let maxImageGrid: CGFloat = 7 - var headerMenu: NCSectionFirstHeader? - var tipViewAccounts: EasyTipView? var tipViewAutoUpload: EasyTipView? - + var headerMenu: NCSectionHeaderMenu? + var headerMenuTransferView = false + var headerMenuButtonsView: Bool = true + var headerRichWorkspaceDisable: Bool = false + // DECLARE var layoutKey = "" var titleCurrentFolder = "" var titlePreviusFolder: String? var enableSearchBar: Bool = false - var headerRichWorkspaceDisable: Bool = false + var groupByField = "name" var emptyImageName: String? var emptyImageColors: [UIColor]? - var headerMenuButtonsView: Bool = true var emptyImage: UIImage? var emptyTitle: String = "" @@ -105,6 +106,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let heightHeaderRecommendations: CGFloat = 160 let heightHeaderSection: CGFloat = 30 + var isTransitioning: Bool = false + var selectableDataSource: [RealmSwiftObject] { dataSource.getMetadataSourceForAllSections() } + var pushed: Bool = false + var emptyDataSet: NCEmptyDataSet? + var session: NCSession.Session { NCSession.shared.getSession(controller: tabBarController) } @@ -180,8 +186,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS view.backgroundColor = .systemBackground collectionView.backgroundColor = .systemBackground - refreshControl.tintColor = NCBrandColor.shared.textColor2 - + refreshControl.tintColor = .gray + + listLayout = NCListLayout() + gridLayout = NCGridLayout() + if enableSearchBar { searchController = UISearchController(searchResultsController: nil) searchController?.searchResultsUpdater = self @@ -190,7 +199,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS searchController?.searchBar.delegate = self searchController?.searchBar.autocapitalizationType = .none navigationItem.searchController = searchController - navigationItem.hidesSearchBarWhenScrolling = true + navigationItem.hidesSearchBarWhenScrolling = false + navigationItem.backBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_back_", comment: ""), style: .plain, target: nil, action: nil) } // Cell @@ -200,12 +210,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS collectionView.register(UINib(nibName: "NCTransferCell", bundle: nil), forCellWithReuseIdentifier: "transferCell") // Header - collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeader") - collectionView.register(UINib(nibName: "NCSectionFirstHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeader") + collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu") collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeader") - collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionHeader") - collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData") - collectionView.register(UINib(nibName: "NCSectionFirstHeaderEmptyData", bundle: nil), forSupplementaryViewOfKind: mediaSectionHeader, withReuseIdentifier: "sectionFirstHeaderEmptyData") // Footer collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter") @@ -222,6 +228,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } } + // Empty + emptyDataSet = NCEmptyDataSet(view: collectionView, offset: getHeaderHeight(), delegate: self) + let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:))) longPressedGesture.minimumPressDuration = 0.5 longPressedGesture.delegate = self @@ -239,6 +248,14 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let dropInteraction = UIDropInteraction(delegate: self) self.navigationController?.navigationItem.leftBarButtonItems?.first?.customView?.addInteraction(dropInteraction) + if(!UserDefaults.standard.bool(forKey: "isInitialPrivacySettingsShowed") || isApplicationUpdated()){ + redirectToPrivacyViewController() + + //set current app version + let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String + UserDefaults.standard.set(appVersion, forKey: "CurrentAppVersion") + } + NotificationCenter.default.addObserver(self, selector: #selector(changeTheming(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadDataSource), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(getServerData(_:)), name: NSNotification.Name(rawValue: global.notificationCenterGetServerData), object: nil) @@ -256,7 +273,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS navigationController?.navigationBar.topItem?.title = titlePreviusFolder } navigationItem.title = titleCurrentFolder - + navigationController?.setNavigationBarAppearance() + navigationController?.navigationBar.prefersLargeTitles = true + navigationController?.setNavigationBarHidden(false, animated: true) + + appDelegate.activeViewController = self isEditMode = false /// Magentacloud branding changes hide user account button on left navigation bar @@ -264,6 +285,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS setNavigationRightItems() layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) + gridLayout.column = CGFloat(layoutForView?.columnGrid ?? 3) if isLayoutList { collectionView?.collectionViewLayout = listLayout self.layoutType = global.layoutList @@ -303,8 +325,13 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(uploadedLivePhoto(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadedLivePhoto), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(uploadCancelFile(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUploadCancelFile), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(updateShare(_:)), name: NSNotification.Name(rawValue: global.notificationCenterUpdateShare), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: global.notificationCenterProgressTask), object: nil) + + // FIXME: iPAD PDF landscape mode iOS 16 + DispatchQueue.main.async { + self.collectionView?.collectionViewLayout.invalidateLayout() + } } override func viewWillDisappear(_ animated: Bool) { @@ -312,7 +339,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NCNetworking.shared.cancelUnifiedSearchFiles() dismissTip() - + pushed = false + toggleSelect(isOn: false) // Cancel Queue & Retrieves Properties NCNetworking.shared.downloadThumbnailQueue.cancelAll() NCNetworking.shared.unifiedSearchQueue.cancelAll() @@ -346,6 +374,19 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS dataSource.removeImageCache() } + + func isApplicationUpdated() -> Bool { + let appVersion = Bundle.main.infoDictionary?["CFBundleInfoDictionaryVersion"] as? String ?? "" + let currentVersion = UserDefaults.standard.string(forKey: "CurrentAppVersion") + return currentVersion != appVersion + } + + func redirectToPrivacyViewController() { + let storyBoard: UIStoryboard = UIStoryboard(name: "NCSettings", bundle: nil) + let newViewController = storyBoard.instantiateViewController(withIdentifier: "privacySettingsNavigation") as! UINavigationController + newViewController.modalPresentationStyle = .fullScreen + self.present(newViewController, animated: true, completion: nil) + } func presentationControllerDidDismiss( _ presentationController: UIPresentationController) { let viewController = presentationController.presentedViewController @@ -394,7 +435,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let error = userInfo["error"] as? NKError, error.errorCode != global.errorNotModified else { return } /// Magentacloud branding changes hide user account button on left navigation bar -// setNavigationLeftItems() + setNavigationLeftItems() } @objc func changeTheming(_ notification: NSNotification) { @@ -434,8 +475,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } self.collectionView.collectionViewLayout.invalidateLayout() - - (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() } @objc func reloadDataSource(_ notification: NSNotification) { @@ -549,6 +588,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NCContentPresenter().showError(error: error) } reloadDataSource() + } else { + collectionView.reloadData() } } @@ -636,7 +677,25 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } @objc func uploadStartFile(_ notification: NSNotification) { - collectionView?.reloadData() + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + let account = userInfo["account"] as? String, + !isSearchingMode, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + // Header view trasfer + if metadata.isTransferInForeground { + NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: 0) + DispatchQueue.main.async { self.collectionView?.reloadData() } + } + + if account == self.session.account, serverUrl == self.serverUrl { + reloadDataSource() + } else { + collectionView?.reloadData() + } } @objc func uploadedFile(_ notification: NSNotification) { @@ -677,295 +736,79 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS collectionView?.reloadData() } } + + @objc func triggerProgressTask(_ notification: NSNotification) { + guard let userInfo = notification.userInfo as NSDictionary?, + let progressNumber = userInfo["progress"] as? NSNumber, + let totalBytes = userInfo["totalBytes"] as? Int64, + let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64, + let ocId = userInfo["ocId"] as? String, + let ocIdTransfer = userInfo["ocIdTransfer"] as? String, + let session = userInfo["session"] as? String + else { return } - @objc func updateShare(_ notification: NSNotification) { - if isSearchingMode { - networkSearch() - } else { - self.dataSource.removeAll() - getServerData() - } - } - - // MARK: - Layout + let chunk: Int = userInfo["chunk"] as? Int ?? 0 + let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false - func setNavigationLeftItems() { - guard layoutKey == global.layoutViewFiles, - let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) else { - return } - let image = utility.loadUserImage(for: tableAccount.user, displayName: tableAccount.displayName, urlBase: tableAccount.urlBase) - let accountButton = AccountSwitcherButton(type: .custom) - let accounts = database.getAllAccountOrderAlias() - var childrenAccountSubmenu: [UIMenuElement] = [] - - accountButton.setImage(image, for: .normal) - accountButton.setImage(image, for: .highlighted) - accountButton.semanticContentAttribute = .forceLeftToRight - accountButton.sizeToFit() - - if !accounts.isEmpty { - let accountActions: [UIAction] = accounts.map { account in - let image = utility.loadUserImage(for: account.user, displayName: account.displayName, urlBase: account.urlBase) - var name: String = "" - var url: String = "" - - if account.alias.isEmpty { - name = account.displayName - url = (URL(string: account.urlBase)?.host ?? "") + let transfer = NCTransferProgress.shared.append(NCTransferProgress.Transfer(ocId: ocId, ocIdTransfer: ocIdTransfer, session: session, chunk: chunk, e2eEncrypted: e2eEncrypted, progressNumber: progressNumber, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected)) + + DispatchQueue.main.async { + if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) { + if NCNetworking.shared.transferInForegorund?.ocId == ocId { + NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue } else { - name = account.alias + NCNetworking.shared.transferInForegorund = NCNetworking.TransferInForegorund(ocId: ocId, progress: progressNumber.floatValue) + self.collectionView.reloadData() } - - let action = UIAction(title: name, image: image, state: account.active ? .on : .off) { _ in - if !account.active { - NCAccount().changeAccount(account.account, userProfile: nil, controller: self.controller) { } - self.setEditMode(false) + self.headerMenu?.progressTransfer.progress = transfer.progressNumber.floatValue + } else { + guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath, + let cell = self.collectionView?.cellForItem(at: indexPath), + let cell = cell as? NCCellProtocol else { return } + if progressNumber.floatValue == 1 && !(cell is NCTransferCell) { + cell.fileProgressView?.isHidden = true + cell.fileProgressView?.progress = .zero + cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCImageCache.images.buttonMore) + if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + cell.writeInfoDateSize(date: metadata.date, size: metadata.size) + } else { + cell.fileInfoLabel?.text = "" + cell.fileSubinfoLabel?.text = "" + } + } else { + cell.fileProgressView?.isHidden = false + cell.fileProgressView?.progress = progressNumber.floatValue + cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop) + let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal + if status == NCGlobal.shared.metadataStatusDownloading { + cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected) + cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes) + } else if status == NCGlobal.shared.metadataStatusUploading { + if totalBytes > 0 { + cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected) + cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ " + self.utilityFileSystem.transformedSize(totalBytes) + } else { + cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected) + cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↑ …" + } } } - - action.subtitle = url - return action - } - - let addAccountAction = UIAction(title: NSLocalizedString("_add_account_", comment: ""), image: utility.loadImage(named: "person.crop.circle.badge.plus", colors: NCBrandColor.shared.iconImageMultiColors)) { _ in - self.appDelegate.openLogin(selector: self.global.introLogin) - } - - let settingsAccountAction = UIAction(title: NSLocalizedString("_account_settings_", comment: ""), image: utility.loadImage(named: "gear", colors: [NCBrandColor.shared.iconImageColor])) { _ in - let accountSettingsModel = NCAccountSettingsModel(controller: self.controller, delegate: self) - let accountSettingsView = NCAccountSettingsView(model: accountSettingsModel) - let accountSettingsController = UIHostingController(rootView: accountSettingsView) - self.present(accountSettingsController, animated: true, completion: nil) - } - - if !NCBrandOptions.shared.disable_multiaccount { - childrenAccountSubmenu.append(addAccountAction) - } - childrenAccountSubmenu.append(settingsAccountAction) - - let addAccountSubmenu = UIMenu(title: "", options: .displayInline, children: childrenAccountSubmenu) - let menu = UIMenu(children: accountActions + [addAccountSubmenu]) - - accountButton.menu = menu - accountButton.showsMenuAsPrimaryAction = true - - accountButton.onMenuOpened = { - self.dismissTip() } } + } - navigationItem.leftItemsSupplementBackButton = true - navigationItem.setLeftBarButtonItems([UIBarButtonItem(customView: accountButton)], animated: true) - - if titlePreviusFolder != nil { - navigationController?.navigationBar.topItem?.title = titlePreviusFolder + @objc func updateShare(_ notification: NSNotification) { + if isSearchingMode { + networkSearch() + } else { + self.dataSource.removeAll() + getServerData() } - - navigationItem.title = titleCurrentFolder } - func setNavigationRightItems() { - guard layoutKey != global.layoutViewTransfers else { return } - let isTabBarHidden = self.tabBarController?.tabBar.isHidden ?? true - let isTabBarSelectHidden = tabBarSelect.isHidden() - - func createMenuActions() -> [UIMenuElement] { - guard let layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return [] } - - let select = UIAction(title: NSLocalizedString("_select_", comment: ""), - image: utility.loadImage(named: "checkmark.circle"), - attributes: (self.dataSource.isEmpty() || NCNetworking.shared.isOffline) ? .disabled : []) { _ in - self.setEditMode(true) - self.collectionView.reloadData() - } - - let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: utility.loadImage(named: "list.bullet"), state: layoutForView.layout == global.layoutList ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutList - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: utility.loadImage(named: "square.grid.2x2"), state: layoutForView.layout == global.layoutGrid ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutGrid - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let mediaSquare = UIAction(title: NSLocalizedString("_media_square_", comment: ""), image: utility.loadImage(named: "square.grid.3x3"), state: layoutForView.layout == global.layoutPhotoSquare ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutPhotoSquare - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let mediaRatio = UIAction(title: NSLocalizedString("_media_ratio_", comment: ""), image: utility.loadImage(named: "rectangle.grid.3x2"), state: layoutForView.layout == self.global.layoutPhotoRatio ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutPhotoRatio - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid, mediaSquare, mediaRatio]) - - let ascending = layoutForView.ascending - let ascendingChevronImage = utility.loadImage(named: ascending ? "chevron.up" : "chevron.down") - let isName = layoutForView.sort == "fileName" - let isDate = layoutForView.sort == "date" - let isSize = layoutForView.sort == "size" - - let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in - - if isName { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - layoutForView.sort = "fileName" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in - - if isDate { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - layoutForView.sort = "date" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in - - if isSize { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - layoutForView.sort = "size" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest]) - - let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: utility.loadImage(named: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in - - layoutForView.directoryOnTop = !layoutForView.directoryOnTop - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: session.account) - let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: utility.loadImage(named: "folder.badge.person.crop", colors: NCBrandColor.shared.iconImageMultiColors), state: personalFilesOnly ? .on : .off) { _ in - - NCKeychain().setPersonalFilesOnly(account: self.session.account, value: !personalFilesOnly) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) - self.setNavigationRightItems() - } - - let showDescriptionKeychain = NCKeychain().showDescription - let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: utility.loadImage(named: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in - - NCKeychain().showDescription = !showDescriptionKeychain - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) - self.setNavigationRightItems() - } - showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" - - if layoutKey == global.layoutViewRecent { - return [select] - } else { - var additionalSubmenu = UIMenu() - if layoutKey == global.layoutViewFiles { - additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription]) - } else { - additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription]) - } - return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] - } - } + // MARK: - Layout - if isEditMode { - /// Magentacloud branding changes hide options on bottom tab bar -// tabBarSelect.update(selectOcId: selectOcId, metadatas: getSelectedMetadatas(), userId: appDelegate.userId) -// tabBarSelect.show() - let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { - self.setEditMode(false) - self.collectionView.reloadData() - } - navigationItem.rightBarButtonItems = [select] - } else if navigationItem.rightBarButtonItems == nil || (!isEditMode && !tabBarSelect.isHidden()) { - /// Magentacloud branding changes hide options on bottom tab bar -// tabBarSelect.hide() - let menuButton = UIBarButtonItem(image: utility.loadImage(named: "ellipsis.circle"), menu: UIMenu(children: createMenuActions())) - menuButton.tintColor = NCBrandColor.shared.iconImageColor - if layoutKey == global.layoutViewFiles { - let notification = UIBarButtonItem(image: utility.loadImage(named: "bell"), style: .plain) { - if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { - viewController.session = self.session - self.navigationController?.pushViewController(viewController, animated: true) - } - } - notification.tintColor = NCBrandColor.shared.iconImageColor - navigationItem.rightBarButtonItems = [menuButton, notification] - } else { - navigationItem.rightBarButtonItems = [menuButton] - } - } else { - navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions()) - } - - if isEditMode { - let more = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain) { self.presentMenu(with: self.createMenuActions())} - navigationItem.rightBarButtonItems = [more] - } else { - let select = UIBarButtonItem(title: NSLocalizedString("_select_", comment: ""), style: UIBarButtonItem.Style.plain) { self.toggleSelect() } - if layoutKey == NCGlobal.shared.layoutViewFiles { - let notification = UIBarButtonItem(image: utility.loadImage(named: "bell"), style: .plain) { - if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { - self.navigationController?.pushViewController(viewController, animated: true) - } - } - notification.tintColor = NCBrandColor.shared.iconImageColor - navigationItem.rightBarButtonItems = [select, notification] - } else { - navigationItem.rightBarButtonItems = [select] - } - } - guard layoutKey == NCGlobal.shared.layoutViewFiles else { return } + func setNavigationLeftItems() { navigationItem.title = titleCurrentFolder } @@ -980,6 +823,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { } + // MARK: - Empty + + func emptyDataSetView(_ view: NCEmptyView) { + + self.emptyDataSet?.setOffset(getHeaderHeight()) + if isSearchingMode { + view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width) + if self.dataSourceTask?.state == .running { + view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "") + } else { + view.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "") + } + view.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "") + } else if self.dataSourceTask?.state == .running { + view.emptyImage.image = UIImage(named: "networkInProgress")?.image(color: .gray, size: UIScreen.main.bounds.width) + view.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "") + view.emptyDescription.text = "" + } else { + if serverUrl.isEmpty { + view.emptyImage.image = emptyImage + view.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "") + view.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "") + } else { + view.emptyImage.image = UIImage(named: "folder_nmcloud") + view.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "") + view.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "") + } + } + } + // MARK: - SEARCH func searchController(enabled: Bool) { @@ -989,7 +862,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS searchController?.searchBar.alpha = 1 } else { searchController?.searchBar.alpha = 0.3 - } } @@ -1023,23 +895,32 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - TAP EVENT - func tapMoreListItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { - tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender) + func tapMoreListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) { + tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender) } - func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { - tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, image: image, sender: sender) + func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) { + tapMoreGridItem(with: ocId, ocIdTransfer: ocIdTransfer, namedButtonMore: namedButtonMore, image: image, sender: sender) } func tapShareListItem(with ocId: String, ocIdTransfer: String, sender: Any) { + if isEditMode { return } guard let metadata = self.database.getMetadataFromOcId(ocId) else { return } - + TealiumHelper.shared.trackEvent(title: "magentacloud-app.filebrowser.sharing", data: ["": ""]) + appDelegate.adjust.trackEvent(TriggerEvent(Sharing.rawValue)) NCActionCenter.shared.openShare(viewController: self, metadata: metadata, page: .sharing) } - func tapMoreGridItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { + func tapMoreGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, image: UIImage?, sender: Any) { + if isEditMode { return } guard let metadata = self.database.getMetadataFromOcId(ocId) else { return } - toggleMenu(metadata: metadata, image: image) + if namedButtonMore == NCGlobal.shared.buttonMoreMore || namedButtonMore == NCGlobal.shared.buttonMoreLock { + toggleMenu(metadata: metadata, image: image) + } else if namedButtonMore == NCGlobal.shared.buttonMoreStop { + Task { + await cancelSession(metadata: metadata) + } + } } func tapRichWorkspace(_ sender: Any) { @@ -1067,13 +948,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS didSelectMetadata(metadata, withOcIds: false) } - func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } - func tapButtonSwitch(_ sender: Any) { guard !isTransitioning else { return } isTransitioning = true - guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return } + guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return } if layoutForView.layout == NCGlobal.shared.layoutGrid { layoutForView.layout = NCGlobal.shared.layoutList @@ -1088,39 +967,24 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS func tapButtonOrder(_ sender: Any) { let sortMenu = NCSortMenu() - sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) - } - - func tapButtonSwitch(_ sender: Any) { - guard !isTransitioning else { return } - isTransitioning = true - - guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return } - - if layoutForView.layout == NCGlobal.shared.layoutGrid { - layoutForView.layout = NCGlobal.shared.layoutList - } else { - layoutForView.layout = NCGlobal.shared.layoutGrid - } - self.layoutForView = NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView) - self.collectionView.reloadData() - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(layoutForView.layout == NCGlobal.shared.layoutList ? self.listLayout : self.gridLayout, animated: true) {_ in self.isTransitioning = false } + sortMenu.toggleMenu(viewController: self, account: session.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) } - func tapButtonOrder(_ sender: Any) { - let sortMenu = NCSortMenu() - sortMenu.toggleMenu(viewController: self, account: appDelegate.account, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) - } + func tapButtonTransfer(_ sender: Any) { } + + func tapMorePhotoItem(with ocId: String, ocIdTransfer: String, image: UIImage?, sender: Any) { } + + func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } - func longPressListItem(with objectId: String, indexPath: IndexPath, gestureRecognizer: UILongPressGestureRecognizer) { - } + func longPressListItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { } - func longPressMoreListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } + func longPressMoreListItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { } func longPressPhotoItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } - func longPressMoreGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } + func longPressGridItem(with ocId: String, ocIdTransfer: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { } + + func longPressMoreGridItem(with ocId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { } @objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) { openMenuItems(with: nil, gestureRecognizer: gestureRecognizer) @@ -1176,6 +1040,20 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - DataSource @objc func reloadDataSource() { + + // get auto upload folder + autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName() + autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: session.urlBase, userId: session.userId, account: session.account) + + // get layout for view + layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) + + // set GroupField for Grid + if !isSearchingMode && layoutForView?.layout == NCGlobal.shared.layoutGrid { + groupByField = "classFile" + } else { + groupByField = "name" + } if isSearchingMode { isDirectoryEncrypted = false } else { @@ -1183,14 +1061,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } DispatchQueue.main.async { - UIView.transition(with: self.collectionView, - duration: 0.20, - options: .transitionCrossDissolve, - animations: { self.collectionView.reloadData() }, - completion: nil) - - (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() self.refreshControl.endRefreshing() + self.collectionView.reloadData() + self.setNavigationRightItems() } } @@ -1291,46 +1164,24 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } } } + + func pushViewController(viewController: UIViewController) { + if pushed { return } - // MARK: - Header size - - func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) { - var headerRichWorkspace: CGFloat = 0 - - func getHeaderHeight() -> CGFloat { - var size: CGFloat = 0 + pushed = true + navigationController?.pushViewController(viewController, animated: true) + } - if isHeaderMenuTransferViewEnabled() != nil { - if !isSearchingMode { - size += global.heightHeaderTransfer - } - } - if headerMenuButtonsView { - size += NCGlobal.shared.heightButtonsView - } - if headerMenuButtonsView { - size += NCGlobal.shared.heightButtonsView - } - return size - } + // MARK: - Header size -// if isRecommendationActived, -// !isSearchingMode, -// NCKeychain().showRecommendedFiles, -// !self.database.getRecommendedFiles(account: self.session.account).isEmpty { -// heightHeaderRecommendations = self.heightHeaderRecommendations -// heightHeaderSection = self.heightHeaderSection -// } - - if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 { - if section == 0 { - return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection) - } else { - return (0, 0, self.heightHeaderSection) - } - } else { - return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) + func isHeaderMenuTransferViewEnabled() -> [tableMetadata]? { + if headerMenuTransferView, + NCNetworking.shared.isOnline, + let results = database.getResultsMetadatas(predicate: NSPredicate(format: "status IN %@", [global.metadataStatusWaitUpload, global.metadataStatusUploading])), + !results.isEmpty { + return Array(results) } + return nil } func sizeForHeaderInSection(section: Int) -> CGSize { @@ -1339,12 +1190,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let isIphone = UIDevice.current.userInterfaceIdiom == .phone if self.dataSource.isEmpty() { - height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset) + height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil) } else if isEditMode || (isLandscape && isIphone) { return CGSize.zero } else { - let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: section) - height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderSection + let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section) + height = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection } return CGSize(width: collectionView.frame.width, height: height) diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift index bd30492ed0..340bcfabb1 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift @@ -28,6 +28,7 @@ import NextcloudKit protocol NCSectionFirstHeaderDelegate: AnyObject { func tapButtonSwitch(_ sender: Any) func tapButtonOrder(_ sender: Any) + func tapButtonMore(_ sender: Any) func tapButtonTransfer(_ sender: Any) func tapRichWorkspace(_ sender: Any) func tapRecommendations(with metadata: tableMetadata) @@ -37,12 +38,14 @@ protocol NCSectionFirstHeaderDelegate: AnyObject { extension NCSectionFirstHeaderDelegate { func tapButtonSwitch(_ sender: Any) {} func tapButtonOrder(_ sender: Any) {} + func tapButtonMore(_ sender: Any) {} } class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegate { @IBOutlet weak var buttonSwitch: UIButton! @IBOutlet weak var buttonOrder: UIButton! + @IBOutlet weak var buttonMore: UIButton! @IBOutlet weak var buttonTransfer: UIButton! @IBOutlet weak var imageButtonTransfer: UIImageView! @IBOutlet weak var labelTransfer: UILabel! @@ -63,11 +66,10 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewRecommendationsHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var textViewRichWorkspace: UITextView! @IBOutlet weak var collectionViewRecommendations: UICollectionView! @IBOutlet weak var labelRecommendations: UILabel! - @IBOutlet weak var labelSection: UILabel! private weak var delegate: NCSectionFirstHeaderDelegate? private let utility = NCUtility() @@ -89,10 +91,11 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat backgroundColor = .clear //Button - buttonSwitch.setImage(UIImage(systemName: "list.bullet"), for: .normal)//!.image(color: NCBrandColor.shared.iconColor, size: 25), for: .normal) + buttonSwitch.setImage(UIImage(systemName: "list.bullet")!.image(color: NCBrandColor.shared.iconColor, size: 25), for: .normal) buttonOrder.setTitle("", for: .normal) buttonOrder.setTitleColor(NCBrandColor.shared.brand, for: .normal) + buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.iconColor, size: 25), for: .normal) // Gradient // gradient.startPoint = CGPoint(x: 0, y: 0.8) @@ -102,7 +105,9 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:))) tap.delegate = self viewRichWorkspace?.addGestureRecognizer(tap) - + viewSeparatorHeightConstraint.constant = 0.5 + viewSeparator.backgroundColor = .separator + markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 15), color: NCBrandColor.shared.textColor) markdownParser.header.font = UIFont.systemFont(ofSize: 25) if let richWorkspaceText = richWorkspaceText { @@ -131,12 +136,12 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat buttonTransfer.setImage(nil, for: .normal) buttonTransfer.layer.cornerRadius = 6 buttonTransfer.layer.masksToBounds = true - imageButtonTransfer.image = NCUtility().loadImage(named: "stop.circle") + imageButtonTransfer.image = UIImage(systemName: "stop.circle") imageButtonTransfer.tintColor = .white labelTransfer.text = "" progressTransfer.progress = 0 - progressTransfer.tintColor = NCBrandColor.shared.brandElement - progressTransfer.trackTintColor = NCBrandColor.shared.brandElement.withAlphaComponent(0.2) + progressTransfer.tintColor = NCBrandColor.shared.brand + progressTransfer.trackTintColor = NCBrandColor.shared.brand.withAlphaComponent(0.2) transferSeparatorBottom.backgroundColor = .separator transferSeparatorBottomHeightConstraint.constant = 0.5 } @@ -160,16 +165,19 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat buttonSwitch.isEnabled = enable buttonOrder.isEnabled = enable + buttonMore.isEnabled = enable } + func buttonMoreIsHidden(_ isHidden: Bool) { + buttonMore.isHidden = isHidden + } + func setImageSwitchList() { - - buttonSwitch.setImage(UIImage(systemName: "list.bullet"), for: .normal)//!.image(color: NCBrandColor.shared.iconColor, width: 20, height: 15), for: .normal) + buttonSwitch.setImage(UIImage(systemName: "list.bullet")!.image(color: NCBrandColor.shared.iconColor, width: 20, height: 15), for: .normal) } func setImageSwitchGrid() { - - buttonSwitch.setImage(UIImage(systemName: "square.grid.2x2")!.image(color: NCBrandColor.shared.iconImageColor, size: 20), for: .normal) + buttonSwitch.setImage(UIImage(systemName: "square.grid.2x2")!.image(color: NCBrandColor.shared.iconColor, size: 20), for: .normal) } func setButtonsView(height: CGFloat) { @@ -240,9 +248,9 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat var image: UIImage? if let ocId, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - image = utility.getIcon(metadata: metadata)?.darken() + image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt256)?.darken() if image == nil { - image = utility.loadImage(named: metadata.iconName, useTypeIconFile: true) + image = UIImage(named: metadata.iconName) buttonTransfer.backgroundColor = .lightGray } else { buttonTransfer.backgroundColor = .clear @@ -254,11 +262,11 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat } } - if heightHeaderSection == 0 { - viewSection.isHidden = true - } else { - viewSection.isHidden = false - } +// if heightHeaderSection == 0 { +// viewSection.isHidden = true +// } else { +// viewSection.isHidden = false +// } self.collectionViewRecommendations.reloadData() } @@ -282,6 +290,10 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat @IBAction func touchUpInsideOrder(_ sender: Any) { delegate?.tapButtonOrder(sender) } + + @IBAction func touchUpInsideMore(_ sender: Any) { + delegate?.tapButtonMore(sender) + } @IBAction func touchUpTransfer(_ sender: Any) { delegate?.tapButtonTransfer(sender) diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib index 528b2f0098..53ceade6de 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib @@ -46,6 +46,9 @@ + + + @@ -177,6 +180,7 @@ + diff --git a/iOSClient/Main/NCActionCenter.swift b/iOSClient/Main/NCActionCenter.swift index 64dd9ce754..d318b6b0d5 100644 --- a/iOSClient/Main/NCActionCenter.swift +++ b/iOSClient/Main/NCActionCenter.swift @@ -105,54 +105,67 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec switch selector { case NCGlobal.shared.selectorLoadFileQuickLook: - - let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) - let fileNameTemp = NSTemporaryDirectory() + metadata.fileNameView - let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNameTemp), isEditingEnabled: true, metadata: metadata) - if let image = UIImage(contentsOfFile: fileNamePath) { - if let data = image.jpegData(compressionQuality: 1) { - do { - try data.write(to: URL(fileURLWithPath: fileNameTemp)) - } catch { - return + DispatchQueue.main.async { + let fileNamePath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) + let fileNameTemp = NSTemporaryDirectory() + metadata.fileNameView + let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNameTemp), isEditingEnabled: true, metadata: metadata) + if let image = UIImage(contentsOfFile: fileNamePath) { + if let data = image.jpegData(compressionQuality: 1) { + do { + try data.write(to: URL(fileURLWithPath: fileNameTemp)) + } catch { + return + } } + let navigationController = UINavigationController(rootViewController: viewerQuickLook) + navigationController.modalPresentationStyle = .fullScreen + controller.present(navigationController, animated: true) + } else { + self.utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNameTemp) + controller.present(viewerQuickLook, animated: true) } - let navigationController = UINavigationController(rootViewController: viewerQuickLook) - navigationController.modalPresentationStyle = .fullScreen - controller.present(navigationController, animated: true) - } else { - self.utilityFileSystem.copyFile(atPath: fileNamePath, toPath: fileNameTemp) - controller.present(viewerQuickLook, animated: true) } case NCGlobal.shared.selectorLoadFileView: - - guard UIApplication.shared.applicationState == .active else { return } - if metadata.contentType.contains("opendocument") && !self.utility.isTypeFileRichDocument(metadata) { - self.openDocumentController(metadata: metadata, controller: controller) - } else if metadata.classFile == NKCommon.TypeClassFile.compress.rawValue || metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue { - self.openDocumentController(metadata: metadata, controller: controller) - } else { - if let viewController = controller.currentViewController() { - let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt1024) - NCViewer().view(viewController: viewController, metadata: metadata, image: image) + DispatchQueue.main.async { + guard UIApplication.shared.applicationState == .active else { return } + if metadata.contentType.contains("opendocument") && !self.utility.isTypeFileRichDocument(metadata) { + self.openDocumentController(metadata: metadata, controller: controller) + } else if metadata.classFile == NKCommon.TypeClassFile.compress.rawValue || metadata.classFile == NKCommon.TypeClassFile.unknow.rawValue { + self.openDocumentController(metadata: metadata, controller: controller) + } else { + if let viewController = controller.currentViewController() { + let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt1024) + NCViewer().view(viewController: viewController, metadata: metadata, image: image, metadatas: [metadata]) + } } } case NCGlobal.shared.selectorOpenIn: - - if UIApplication.shared.applicationState == .active { - self.openDocumentController(metadata: metadata, controller: controller) + DispatchQueue.main.async { + if UIApplication.shared.applicationState == .active { + self.openDocumentController(metadata: metadata, controller: controller) + } } + case NCGlobal.shared.selectorPrint: + // waiting close menu + // https://github.com/nextcloud/ios/issues/2278 +// DispatchQueue.main.async { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.printDocument(metadata: metadata) + } + case NCGlobal.shared.selectorSaveAlbum: - - self.saveAlbum(metadata: metadata, controller: controller) - + DispatchQueue.main.async { + self.saveAlbum(metadata: metadata, controller: controller) + } + case NCGlobal.shared.selectorSaveAsScan: - - self.saveAsScan(metadata: metadata, controller: controller) - + DispatchQueue.main.async { + self.saveAsScan(metadata: metadata, controller: controller) + } + case NCGlobal.shared.selectorOpenDetail: NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterOpenMediaDetail, userInfo: ["ocId": metadata.ocId]) @@ -190,19 +203,23 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec database.setMetadatasSessionInWaitDownload(metadatas: metadatasSynchronizationOffline, session: NCNetworking.shared.sessionDownloadBackground, selector: NCGlobal.shared.selectorSynchronizationOffline) + AnalyticsHelper.shared.trackEventWithMetadata(eventName: .EVENT__OFFLINE_AVAILABLE, metadata: metadata) } } func viewerFile(account: String, fileId: String, viewController: UIViewController) { var downloadRequest: DownloadRequest? - let hud = NCHud(viewController.tabBarController?.view) - - if let metadata = database.getMetadataFromFileId(fileId) { + var hud = NCHud() + DispatchQueue.main.async { + hud = NCHud(viewController.tabBarController?.view) + } + if let metadata = database.getResultMetadataFromFileId(fileId) { + let metadata = tableMetadata(value: metadata) do { let attr = try FileManager.default.attributesOfItem(atPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) let fileSize = attr[FileAttributeKey.size] as? UInt64 ?? 0 if fileSize > 0 { - NCViewer().view(viewController: viewController, metadata: metadata) + NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata]) return } } catch { @@ -230,7 +247,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) if metadata.isAudioOrVideo { - NCViewer().view(viewController: viewController, metadata: metadata) + NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata]) } else { hud.show() NextcloudKit.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, account: account, requestHandler: { request in @@ -253,7 +270,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec etag: etag) if account == accountDownload, error == .success { self.database.addLocalFile(metadata: metadata) - NCViewer().view(viewController: viewController, metadata: metadata) + NCViewer().view(viewController: viewController, metadata: metadata, metadatas: [metadata]) } } } @@ -300,7 +317,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName var page = page - NCActivityIndicator.shared.start(backgroundView: viewController.view) + DispatchQueue.main.async { NCActivityIndicator.shared.start(backgroundView: viewController.view) } NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account, queue: .main) { account, metadata, error in NCActivityIndicator.shared.stop() @@ -456,12 +473,50 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } } + // MARK: - Print + + func printDocument(metadata: tableMetadata) { + + let fileNameURL = URL(fileURLWithPath: utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) + let printController = UIPrintInteractionController.shared + let printInfo = UIPrintInfo(dictionary: nil) + + printInfo.jobName = fileNameURL.lastPathComponent + printInfo.outputType = metadata.isImage ? .photo : .general + printController.printInfo = printInfo + printController.showsNumberOfCopies = true + + guard !UIPrintInteractionController.canPrint(fileNameURL) else { + printController.printingItem = fileNameURL + printController.present(animated: true) + return + } + + // can't print without data + guard let data = try? Data(contentsOf: fileNameURL) else { return } + + if let svg = SVGKImage(data: data) { + printController.printingItem = svg.uiImage + printController.present(animated: true) + return + } + + guard let text = String(data: data, encoding: .utf8) else { return } + let formatter = UISimpleTextPrintFormatter(text: text) + formatter.perPageContentInsets.top = 72 + formatter.perPageContentInsets.bottom = 72 + formatter.perPageContentInsets.left = 72 + formatter.perPageContentInsets.right = 72 + printController.printFormatter = formatter + printController.present(animated: true) + } + // MARK: - Copy & Paste - func copyPasteboard(pasteboardOcIds: [String], viewController: UIViewController) { + func copyPasteboard(pasteboardOcIds: [String], controller: NCMainTabBarController?) { var items = [[String: Any]]() guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - let hudView = viewController.view + let hudView = controller var fractionCompleted: Float = 0 // getting file data can take some time and block the main queue @@ -477,18 +532,18 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec } // do 5 downloads in parallel to optimize efficiency - let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, hudView: hudView) + let processor = ParallelWorker(n: 5, titleKey: "_downloading_", totalTasks: downloadMetadatas.count, controller: hudView) for metadata in downloadMetadatas { processor.execute { completion in guard let metadata = NCManageDatabase.shared.setMetadatasSessionInWaitDownload(metadatas: [metadata], - session: NextcloudKit.shared.nkCommonInstance.sessionIdentifierDownload, + session: NextcloudKit.shared.nkCommonInstance.identifierSessionDownload, selector: "") else { return completion() } NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: false) { } requestHandler: { _ in } progressHandler: { progress in if Float(progress.fractionCompleted) > fractionCompleted || fractionCompleted == 0 { - processor.hud?.progress = Float(progress.fractionCompleted) + processor.hud.progress(progress.fractionCompleted) fractionCompleted = Float(progress.fractionCompleted) } } completion: { _, _ in @@ -606,7 +661,7 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec // MARK: - NCSelect + Delegate - func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool, session: NCSession.Session) { + func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { if let serverUrl, !items.isEmpty { if copy { var metadataServerUrl: String = "" diff --git a/iOSClient/Menu/NCMenuAction.swift b/iOSClient/Menu/NCMenuAction.swift index 691164f308..bbed593fc8 100644 --- a/iOSClient/Menu/NCMenuAction.swift +++ b/iOSClient/Menu/NCMenuAction.swift @@ -25,6 +25,7 @@ import Foundation import UIKit import NextcloudKit +import SVGKit class NCMenuAction { let accessibilityIdentifier: String? @@ -86,7 +87,7 @@ extension NCMenuAction { static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction { NCMenuAction( title: NSLocalizedString("_select_all_", comment: ""), - icon: NCUtility().loadImage(named: "checkmark.circle.fill", colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: "checkmark.circle.fill", colors: [NCBrandColor.shared.iconColor]), action: { _ in action() } ) } @@ -95,7 +96,7 @@ extension NCMenuAction { static func cancelAction(action: @escaping () -> Void) -> NCMenuAction { NCMenuAction( title: NSLocalizedString("_cancel_", comment: ""), - icon: NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: "xmark", colors: [NCBrandColor.shared.iconColor]), action: { _ in action() } ) } @@ -106,7 +107,7 @@ extension NCMenuAction { var message = NSLocalizedString("_want_delete_", comment: "") var icon = "trash" var destructive = false - var color = NCBrandColor.shared.iconImageColor + var color = NCBrandColor.shared.iconColor let permissions = NCPermissions() if selectedMetadatas.count > 1 { @@ -160,8 +161,8 @@ extension NCMenuAction { /// Open "share view" (activity VC) to open files in another app static func share(selectedMetadatas: [tableMetadata], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { NCMenuAction( - title: NSLocalizedString("_share_", comment: ""), - icon: NCUtility().loadImage(named: "square.and.arrow.up", colors: [NCBrandColor.shared.iconImageColor]), + title: NSLocalizedString("_open_in_", comment: ""), + icon: NCUtility().loadImage(named: "open_file",colors: [NCBrandColor.shared.iconColor]), order: order, action: { _ in NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas, controller: controller) @@ -174,7 +175,7 @@ extension NCMenuAction { static func setAvailableOfflineAction(selectedMetadatas: [tableMetadata], isAnyOffline: Bool, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { NCMenuAction( title: isAnyOffline ? NSLocalizedString("_remove_available_offline_", comment: "") : NSLocalizedString("_set_available_offline_", comment: ""), - icon: NCUtility().loadImage(named: "icloud.and.arrow.down", colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: "offlineMenu", colors: [NCBrandColor.shared.iconColor]), order: order, action: { _ in if !isAnyOffline, selectedMetadatas.count > 3 { @@ -197,10 +198,10 @@ extension NCMenuAction { } /// Copy files to pasteboard - static func copyAction(selectOcId: [String], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { + static func copyAction(fileSelect: [String], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { NCMenuAction( title: NSLocalizedString("_copy_file_", comment: ""), - icon: NCUtility().loadImage(named: "copy", colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: "copy", colors: [NCBrandColor.shared.iconColor]), order: order, action: { _ in NCActionCenter.shared.copyPasteboard(pasteboardOcIds: selectOcId, viewController: viewController) @@ -211,31 +212,33 @@ extension NCMenuAction { /// Open view that lets the user move or copy the files within Nextcloud - static func moveOrCopyAction(selectedMetadatas: [tableMetadata], viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { + static func moveOrCopyAction(selectedMetadatas: [tableMetadata], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { NCMenuAction( title: NSLocalizedString("_move_or_copy_", comment: ""), - icon: NCUtility().loadImage(named: "rectangle.portrait.and.arrow.right", colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: "move", colors: [NCBrandColor.shared.iconColor]), order: order, action: { _ in - var fileNameError: NKError? - - for metadata in selectedMetadatas { - if let sceneIdentifier = metadata.sceneIdentifier, - let controller = SceneManager.shared.getController(sceneIdentifier: sceneIdentifier), - let checkError = FileNameValidator.checkFileName(metadata.fileNameView, account: controller.account) { - - fileNameError = checkError - break - } - } - - if let fileNameError { - viewController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true, completion: nil) - } else { - let controller = viewController.tabBarController as? NCMainTabBarController - NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller) - completion?() - } +// var fileNameError: NKError? +// +// for metadata in selectedMetadatas { +// if let sceneIdentifier = metadata.sceneIdentifier, +// let controller = SceneManager.shared.getController(sceneIdentifier: sceneIdentifier), +// let checkError = FileNameValidator.checkFileName(metadata.fileNameView, account: controller.account) { +// +// fileNameError = checkError +// break +// } +// } +// +// if let fileNameError { +// viewController.present(UIAlertController.warning(message: "\(fileNameError.errorDescription) \(NSLocalizedString("_please_rename_file_", comment: ""))"), animated: true, completion: nil) +// } else { +// let controller = viewController.tabBarController as? NCMainTabBarController +// NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller) +// completion?() +// } + NCActionCenter.shared.openSelectView(items: selectedMetadatas, controller: controller) + completion?() } ) } @@ -251,7 +254,7 @@ extension NCMenuAction { let imageName = !shouldLock ? "lock_open" : "lock" return NCMenuAction( title: NSLocalizedString(titleKey, comment: ""), - icon: NCUtility().loadImage(named: imageName, colors: [NCBrandColor.shared.iconImageColor]), + icon: NCUtility().loadImage(named: imageName, colors: [NCBrandColor.shared.iconColor]), order: order, action: { _ in for metadata in metadatas where metadata.lock != shouldLock { @@ -261,4 +264,103 @@ extension NCMenuAction { } ) } + + /// Open "share view" (activity VC) to open files in another app + static func openInAction(selectedMetadatas: [tableMetadata], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { + NCMenuAction( + title: NSLocalizedString("_open_in_", comment: ""), + icon: NCUtility().loadImage(named: "open_file",colors: [NCBrandColor.shared.iconColor]), + order: order, + action: { _ in + NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas, controller: controller) + completion?() + } + ) + } + + /// Save selected files to user's photo library + static func saveMediaAction(selectedMediaMetadatas: [tableMetadata], controller: NCMainTabBarController?, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction { + var title: String = NSLocalizedString("_save_selected_files_", comment: "") + var icon = NCUtility().loadImage(named: "save_files",colors: [NCBrandColor.shared.iconColor]) + if selectedMediaMetadatas.allSatisfy({ NCManageDatabase.shared.getMetadataLivePhoto(metadata: $0) != nil }) { + title = NSLocalizedString("_livephoto_save_", comment: "") + icon = NCUtility().loadImage(named: "livephoto") + } + + return NCMenuAction( + title: title, + icon: icon, + order: order, + action: { _ in + for metadata in selectedMediaMetadatas { + if let metadataMOV = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata) { + NCNetworking.shared.saveLivePhotoQueue.addOperation(NCOperationSaveLivePhoto(metadata: metadata, metadataMOV: metadataMOV, hudView: controller?.view ?? UIView())) + } else { + if NCUtilityFileSystem().fileProviderStorageExists(metadata) { + NCActionCenter.shared.saveAlbum(metadata: metadata, controller: controller) + } else { + if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty { + NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorSaveAlbum)) + } + } + } + } + completion?() + } + ) + } + + /// Open AirPrint view to print a single file + static func printAction(metadata: tableMetadata, order: Int = 0) -> NCMenuAction { + NCMenuAction( + title: NSLocalizedString("_print_", comment: ""), + icon: NCUtility().loadImage(named: "printer", colors: [NCBrandColor.shared.iconColor]), + order: order, + action: { _ in + if NCUtilityFileSystem().fileProviderStorageExists(metadata) { + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": NCGlobal.shared.selectorPrint, "error": NKError(), "account": metadata.account, "ocIdTransfer": metadata.ocIdTransfer]) + } else { + NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorPrint)) + } + } + ) + } + + // MARK: - Print + + static func printDocument(metadata: tableMetadata) { + + let fileNameURL = URL(fileURLWithPath: NCUtilityFileSystem().getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) + let printController = UIPrintInteractionController.shared + let printInfo = UIPrintInfo(dictionary: nil) + + printInfo.jobName = fileNameURL.lastPathComponent + printInfo.outputType = metadata.isImage ? .photo : .general + printController.printInfo = printInfo + printController.showsNumberOfCopies = true + + guard !UIPrintInteractionController.canPrint(fileNameURL) else { + printController.printingItem = fileNameURL + printController.present(animated: true) + return + } + + // can't print without data + guard let data = try? Data(contentsOf: fileNameURL) else { return } + + if let svg = SVGKImage(data: data) { + printController.printingItem = svg.uiImage + printController.present(animated: true) + return + } + + guard let text = String(data: data, encoding: .utf8) else { return } + let formatter = UISimpleTextPrintFormatter(text: text) + formatter.perPageContentInsets.top = 72 + formatter.perPageContentInsets.bottom = 72 + formatter.perPageContentInsets.left = 72 + formatter.perPageContentInsets.right = 72 + printController.printFormatter = formatter + printController.present(animated: true) + } } diff --git a/iOSClient/Menu/NCSortMenu.swift b/iOSClient/Menu/NCSortMenu.swift index 3aaadb4dcd..6b62e33b31 100644 --- a/iOSClient/Menu/NCSortMenu.swift +++ b/iOSClient/Menu/NCSortMenu.swift @@ -30,6 +30,7 @@ class NCSortMenu: NSObject { private var sortButton: UIButton? private var serverUrl: String = "" private var hideDirectoryOnTop: Bool? + private var account: String = "" private var key = "" @@ -39,6 +40,7 @@ class NCSortMenu: NSObject { self.sortButton = sortButton self.serverUrl = serverUrl self.hideDirectoryOnTop = hideDirectoryOnTop + self.account = account guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: account, key: key, serverUrl: serverUrl) else { return } var actions = [NCMenuAction]() @@ -47,10 +49,10 @@ class NCSortMenu: NSObject { if layoutForView.ascending { title = NSLocalizedString("_order_by_name_z_a_", comment: "") - icon = UIImage(named: "sortFileNameZA")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortFileNameZA")!.image(color: NCBrandColor.shared.iconColor, size: 50) } else { title = NSLocalizedString("_order_by_name_a_z_", comment: "") - icon = UIImage(named: "sortFileNameAZ")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortFileNameAZ")!.image(color: NCBrandColor.shared.iconColor, size: 50) } actions.append( @@ -69,10 +71,10 @@ class NCSortMenu: NSObject { if layoutForView.ascending { title = NSLocalizedString("_order_by_date_more_recent_", comment: "") - icon = UIImage(named: "sortDateMoreRecent")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortDateMoreRecent")!.image(color: NCBrandColor.shared.iconColor, size: 50) } else { title = NSLocalizedString("_order_by_date_less_recent_", comment: "") - icon = UIImage(named: "sortDateLessRecent")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortDateLessRecent")!.image(color: NCBrandColor.shared.iconColor, size: 50) } actions.append( @@ -91,10 +93,10 @@ class NCSortMenu: NSObject { if layoutForView.ascending { title = NSLocalizedString("_order_by_size_largest_", comment: "") - icon = UIImage(named: "sortLargest")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortLargest")!.image(color: NCBrandColor.shared.iconColor, size: 50) } else { title = NSLocalizedString("_order_by_size_smallest_", comment: "") - icon = UIImage(named: "sortSmallest")!.image(color: NCBrandColor.shared.iconImageColor, size: 50) + icon = UIImage(named: "sortSmallest")!.image(color: NCBrandColor.shared.iconColor, size: 50) } actions.append( @@ -115,11 +117,12 @@ class NCSortMenu: NSObject { actions.append( NCMenuAction( title: NSLocalizedString("_directory_on_top_no_", comment: ""), - icon: UIImage(named: "foldersOnTop")!.image(color: NCBrandColor.shared.iconImageColor, size: 50), + icon: UIImage(named: "foldersOnTop")!.image(color: NCBrandColor.shared.iconColor, size: 50), selected: layoutForView.directoryOnTop, on: layoutForView.directoryOnTop, action: { _ in layoutForView.directoryOnTop = !layoutForView.directoryOnTop + NCKeychain().setDirectoryOnTop(account: self.account, value: layoutForView.directoryOnTop) self.actionMenu(layoutForView: layoutForView) } ) @@ -144,6 +147,11 @@ class NCSortMenu: NSObject { self.sortButton?.setTitle(NSLocalizedString(layoutForView.titleButtonHeader, comment: ""), for: .normal) NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.account, + "serverUrl": self.serverUrl, + "layoutForView": layoutForView]) NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) } } diff --git a/iOSClient/Offline/NCOffline.swift b/iOSClient/Offline/NCOffline.swift index 08a73ae26a..410c50bc93 100644 --- a/iOSClient/Offline/NCOffline.swift +++ b/iOSClient/Offline/NCOffline.swift @@ -34,8 +34,8 @@ class NCOffline: NCCollectionViewCommon { layoutKey = NCGlobal.shared.layoutViewOffline enableSearchBar = false headerRichWorkspaceDisable = true - emptyImageName = "icloud.and.arrow.down" - emptyImage = UIImage(named: "folder") + emptyImageName = "folder_nmcloud" + emptyImage = UIImage(named: "folder_nmcloud") emptyTitle = "_files_no_files_" emptyDescription = "_tutorial_offline_view_" emptyDataPortaitOffset = 30