Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
AABD0C8A2D5F67A400F009E6 /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C892D5F67A200F009E6 /* XCUIElement.swift */; };
AABD0C9B2D5F73FC00F009E6 /* Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABD0C9A2D5F73FA00F009E6 /* Placeholder.swift */; };
AAE330042D2ED20200B04903 /* NCShareNavigationTitleSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE330032D2ED1FF00B04903 /* NCShareNavigationTitleSetting.swift */; };
AB6000012F60000100FE2775 /* NCTagEditorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB6000002F60000100FE2775 /* NCTagEditorModel.swift */; };
AB6000032F60000200FE2775 /* NCTagEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB6000022F60000200FE2775 /* NCTagEditorView.swift */; };
AF1A9B6427D0CA1E00F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; };
AF1A9B6527D0CC0500F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; };
AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; };
Expand Down Expand Up @@ -1209,6 +1211,8 @@
AACCAB632CFE04F700DA1786 /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Localizable.strings; sourceTree = "<group>"; };
AACCAB642CFE04F700DA1786 /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/InfoPlist.strings; sourceTree = "<group>"; };
AAE330032D2ED1FF00B04903 /* NCShareNavigationTitleSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareNavigationTitleSetting.swift; sourceTree = "<group>"; };
AB6000002F60000100FE2775 /* NCTagEditorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTagEditorModel.swift; sourceTree = "<group>"; };
AB6000022F60000200FE2775 /* NCTagEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTagEditorView.swift; sourceTree = "<group>"; };
AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Extension.swift"; sourceTree = "<group>"; };
AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCell.swift; sourceTree = "<group>"; };
AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+DataSource.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1261,6 +1265,7 @@
F34E1AD82ECC839100FA10C3 /* EmojiTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiTextField.swift; sourceTree = "<group>"; };
F34E1ADA2ECC842200FA10C3 /* NCStatusMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCStatusMessageModel.swift; sourceTree = "<group>"; };
F351D1A52D0AF24A00930F94 /* PHAssetCollection+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAssetCollection+Extension.swift"; sourceTree = "<group>"; };
F35746602F6B0D27009F9F5A /* NextcloudKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NextcloudKit; path = ../NextcloudKit; sourceTree = SOURCE_ROOT; };
F359D8662A7D03420023F405 /* NCUtility+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Exif.swift"; sourceTree = "<group>"; };
F36C514D2E89393C0097E5F7 /* UIView+BlurVibrancy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+BlurVibrancy.swift"; sourceTree = "<group>"; };
F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SelectTabBarDelegate.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2369,6 +2374,8 @@
F769454722E9F20D000A798A /* NCShareNetworking.swift */,
AA8E041C2D300FDE00E7E89C /* NCShareNetworkingDelegate.swift */,
F769453F22E9F077000A798A /* NCSharePaging.swift */,
AB6000002F60000100FE2775 /* NCTagEditorModel.swift */,
AB6000022F60000200FE2775 /* NCTagEditorView.swift */,
F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */,
F769453B22E9CFFF000A798A /* NCShareUserCell.xib */,
AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */,
Expand Down Expand Up @@ -3277,6 +3284,7 @@
C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */,
C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */,
F7F1FBA62E27D13700C79E20 /* Frameworks */,
F35746602F6B0D27009F9F5A /* NextcloudKit */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -4699,6 +4707,8 @@
AA8D316F2D4123B200FE2775 /* NCShareDownloadLimitTableViewController.swift in Sources */,
AA8D31702D4123B200FE2775 /* DownloadLimitViewModel.swift in Sources */,
AA8D31712D4123B200FE2775 /* NCShareDownloadLimitViewController.swift in Sources */,
AB6000012F60000100FE2775 /* NCTagEditorModel.swift in Sources */,
AB6000032F60000200FE2775 /* NCTagEditorView.swift in Sources */,
AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */,
F77BC3ED293E528A005F2B08 /* NCConfigServer.swift in Sources */,
F7A560422AE1593700BE8FD6 /* NCOperationSaveLivePhoto.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Share/NCShareExtension+DataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ extension NCShareExtension: UICollectionViewDataSource {
cell.imageStatus.image = utility.loadImage(named: "livephoto", colors: [NCBrandColor.shared.iconImageColor2])
}

cell.setTags(tags: Array(metadata.tags))
cell.setTags(tags: Array(metadata.tags), account: metadata.account)

cell.separator.isHidden = collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1

Expand Down
11 changes: 11 additions & 0 deletions iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,17 @@ extension NCManageDatabase {
}
}

func setMetadataTagsAsync(ocId: String, tags: [String]) async {
await core.performRealmWriteAsync { realm in
guard let metadata = realm.object(ofType: tableMetadata.self, forPrimaryKey: ocId) else {
return
}

metadata.tags.removeAll()
metadata.tags.append(objectsIn: tags)
}
}

func moveMetadataAsync(ocId: String, serverUrlTo: String) async {
await core.performRealmWriteAsync { realm in
if let result = realm.objects(tableMetadata.self)
Expand Down
84 changes: 80 additions & 4 deletions iOSClient/Main/Collection Common/Cell/NCListCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ protocol NCListCellDelegate: AnyObject {
}

class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainProtocol {
private static var tagColorsByAccount: [String: [String: NKTag]] = [:]
private static var loadingTagColorsForAccounts: Set<String> = []

@IBOutlet weak var imageItem: UIImageView!
@IBOutlet weak var imageSelect: UIImageView!
@IBOutlet weak var imageStatus: UIImageView!
Expand All @@ -24,8 +27,8 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainP
@IBOutlet weak var labelInfo: UILabel!
@IBOutlet weak var labelSubinfo: UILabel!
@IBOutlet weak var labelInfoSeparator: UILabel!
@IBOutlet weak var tag0: UILabel!
@IBOutlet weak var tag1: UILabel!
@IBOutlet weak var tag0: PaddedAndBorderedLabel!
@IBOutlet weak var tag1: PaddedAndBorderedLabel!
@IBOutlet weak var labelExtension: UILabel!

@IBOutlet weak var buttonShared: UIButton!
Expand All @@ -38,6 +41,8 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainP
@IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!

weak var delegate: NCListCellDelegate?
private var currentTagTokens: [String] = []
private var currentTagAccount: String = ""

// Cell Protocol
var metadata: tableMetadata? {
Expand Down Expand Up @@ -113,6 +118,8 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainP
labelSubinfo.text = ""
tag0.text = ""
tag1.text = ""
currentTagTokens = []
currentTagAccount = ""

// Dynamic Type Font Configuration
//
Expand Down Expand Up @@ -258,7 +265,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainP
accessibilityValue = value
}

func setTags(tags: [String]) {
func setTags(tags: [String], account: String) {
currentTagTokens = tags
currentTagAccount = account
applyDefaultTagBorderStyle()

if tags.isEmpty {
tag0.isHidden = true
tag1.isHidden = true
Expand All @@ -280,6 +291,71 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellMainP
}
}
}

applyTagBorderColorsIfAvailable()
loadTagColorsIfNeeded(account: account)
}

private func applyDefaultTagBorderStyle() {
tag0.backgroundColor = .clear
tag1.backgroundColor = .clear
tag0.borderColor = .systemGray5
tag1.borderColor = .systemGray5
tag0.textColor = .systemGray
tag1.textColor = .systemGray
tag0.setNeedsDisplay()
tag1.setNeedsDisplay()
}

private func applyTagBorderColorsIfAvailable() {
guard !currentTagTokens.isEmpty,
let lookup = NCListCell.tagColorsByAccount[currentTagAccount],
let firstToken = currentTagTokens.first,
let colorHex = lookup[firstToken]?.color,
let color = UIColor(hex: colorHex) else {
return
}

tag0.borderColor = color
tag0.textColor = color
if !tag1.isHidden {
tag1.borderColor = .systemGray5
tag1.textColor = .systemGray
}
tag0.setNeedsDisplay()
tag1.setNeedsDisplay()
}

private func loadTagColorsIfNeeded(account: String) {
guard !account.isEmpty else {
return
}
if NCListCell.tagColorsByAccount[account] != nil || NCListCell.loadingTagColorsForAccounts.contains(account) {
return
}

NCListCell.loadingTagColorsForAccounts.insert(account)
Task { [weak self] in
let result = await NextcloudKit.shared.getTags(account: account)
DispatchQueue.main.async {
NCListCell.loadingTagColorsForAccounts.remove(account)
guard result.error == .success, let tags = result.tags else {
return
}

var lookup: [String: NKTag] = [:]
for tag in tags {
lookup[tag.id] = tag
lookup[tag.name] = tag
}
NCListCell.tagColorsByAccount[account] = lookup

guard let self, self.currentTagAccount == account else {
return
}
self.applyTagBorderColorsIfAvailable()
}
}
}

func setIconOutlines() {
Expand Down Expand Up @@ -509,7 +585,7 @@ extension NCCollectionViewCommon {
}

// TAGS
cell.setTags(tags: Array(metadata.tags))
cell.setTags(tags: Array(metadata.tags), account: metadata.account)

// SearchingMode - TAG Separator Hidden
if isSearchingMode {
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Select/NCSelect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ extension NCSelect: UICollectionViewDataSource {
}

// Add TAGS
cell.setTags(tags: Array(metadata.tags))
cell.setTags(tags: Array(metadata.tags), account: metadata.account)

return cell
}
Expand Down
79 changes: 78 additions & 1 deletion iOSClient/Share/NCShareHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import UIKit
import TagListView
import SwiftUI
import NextcloudKit

class NCShareHeader: UIView {
@IBOutlet weak var imageView: UIImageView!
Expand All @@ -33,10 +35,14 @@ class NCShareHeader: UIView {
@IBOutlet weak var fileNameTopConstraint: NSLayoutConstraint!
@IBOutlet weak var tagListView: TagListView!

private var metadata = tableMetadata()

private var heightConstraintWithImage: NSLayoutConstraint?
private var heightConstraintWithoutImage: NSLayoutConstraint?
private var currentTagsByToken: [String: NKTag] = [:]

func setupUI(with metadata: tableMetadata) {
self.metadata = metadata.detachedCopy()
let utilityFileSystem = NCUtilityFileSystem()
if let image = NCUtility().getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt1024, userId: metadata.userId, urlBase: metadata.urlBase) {
fullWidthImageView.image = image
Expand Down Expand Up @@ -64,7 +70,8 @@ class NCShareHeader: UIView {
info.textColor = NCBrandColor.shared.textColor2
info.text = utilityFileSystem.transformedSize(metadata.size) + ", " + NCUtility().getRelativeDateTitle(metadata.date as Date)

tagListView.addTags(Array(metadata.tags))
refreshTags(Array(metadata.tags))
loadTagColors()

setNeedsLayout()
layoutIfNeeded()
Expand All @@ -75,4 +82,74 @@ class NCShareHeader: UIView {
imageView.isHidden = traitCollection.verticalSizeClass != .compact
}
}

func presentTagEditor(from sourceViewController: UIViewController, onApplied: (([NKTag]) -> Void)? = nil) {
let editor = NCTagEditorView(
metadata: metadata.detachedCopy(),
initialTags: Array(metadata.tags),
windowScene: sourceViewController.view.window?.windowScene,
onApplied: { [weak self] tags in
self?.metadata.tags.removeAll()
self?.metadata.tags.append(objectsIn: tags.map(\.name))
self?.refreshTags(tags.map(\.name), tagModels: tags)
onApplied?(tags)
}
)
let hosting = UIHostingController(rootView: editor)
hosting.title = NSLocalizedString("_tags_", comment: "")
if let sheet = hosting.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
}
sourceViewController.present(hosting, animated: true)
}

private func refreshTags(_ tags: [String], tagModels: [NKTag]? = nil) {
if let tagModels {
var tagsByToken: [String: NKTag] = [:]
for tag in tagModels {
tagsByToken[tag.id] = tag
tagsByToken[tag.name] = tag
}
currentTagsByToken = tagsByToken
}

tagListView.removeAllTags()
for tagToken in tags {
let matchedTag = currentTagsByToken[tagToken]
let displayName = matchedTag?.name ?? tagToken

let tagView = tagListView.addTag(displayName)
if let colorHex = matchedTag?.color, let color = UIColor(hex: colorHex) {
tagView.tagBackgroundColor = .clear
tagView.borderColor = color
tagView.textColor = color
tagView.selectedTextColor = color
}
}
}

private func loadTagColors() {
let account = metadata.account
let selectedTokens = Set(Array(metadata.tags))
guard !account.isEmpty, !selectedTokens.isEmpty else {
return
}

Task { [weak self] in
guard let self else { return }
let result = await NextcloudKit.shared.getTags(account: account)
guard result.error == .success, let allTags = result.tags else {
return
}

let selectedTags = allTags.filter { tag in
selectedTokens.contains(tag.id) || selectedTokens.contains(tag.name)
}

DispatchQueue.main.async {
self.refreshTags(Array(self.metadata.tags), tagModels: selectedTags)
}
}
}
}
22 changes: 22 additions & 0 deletions iOSClient/Share/NCSharePaging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ class NCSharePaging: UIViewController {

navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_close_", comment: ""), style: .plain, target: self, action: #selector(exitTapped(_:)))

let manageTagsAction = UIAction(title: NSLocalizedString("_edit_tags_", comment: ""), image: UIImage(systemName: "tag")) { [weak self] _ in
self?.editTagsTapped(nil)
}

let moreButton = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: nil, action: nil)
moreButton.menu = UIMenu(children: [manageTagsAction])
navigationItem.rightBarButtonItem = moreButton

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(notification:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
Expand Down Expand Up @@ -168,6 +176,20 @@ class NCSharePaging: UIViewController {
self.dismiss(animated: true, completion: nil)
}

@objc func editTagsTapped(_ sender: Any?) {
guard let header = (pagingViewController.view as? NCSharePagingView)?.header else {
return
}

header.presentTagEditor(from: self) { [weak self] tags in
guard let self else { return }
self.metadata.tags.removeAll()
self.metadata.tags.append(objectsIn: tags.map(\.name))
self.pagingViewController.metadata.tags.removeAll()
self.pagingViewController.metadata.tags.append(objectsIn: tags.map(\.name))
}
}

@objc func applicationDidEnterBackground(notification: Notification) {
self.dismiss(animated: false, completion: nil)
}
Expand Down
Loading