Skip to content
Open
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
12 changes: 12 additions & 0 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@
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 */; };
F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; };
Expand Down Expand Up @@ -1221,6 +1226,9 @@
AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = "<group>"; };
AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = "<group>"; };
AFCE353827E5DE0400FEA6C2 /* Shareable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shareable.swift; sourceTree = "<group>"; };
AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = "<group>"; };
B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift"; sourceTree = "<group>"; };
B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSortMenu.swift; sourceTree = "<group>"; };
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 = "<group>"; };
Expand Down Expand Up @@ -1986,6 +1994,7 @@
F376A3732E5CC5FF0067EE25 /* ContextMenuActions.swift */,
F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */,
F78C6FDD296D677300C952C3 /* NCContextMenu.swift */,
B5C9801F2DAD201A0041B146 /* NCSortMenu.swift */,
3704EB2923D5A58400455C5B /* NCMenu.storyboard */,
371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */,
AF935066276B84E700BD078F /* NCMenu+FloatingPanel.swift */,
Expand Down Expand Up @@ -2464,6 +2473,7 @@
F7603298252F0E550015A421 /* Collection Common */ = {
isa = PBXGroup;
children = (
B5C9801D2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */,
F75FE06B2BB01D0D00A0EFEF /* Cell */,
F78ACD50219046AC0088454D /* Section Header Footer */,
F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */,
Expand Down Expand Up @@ -4470,6 +4480,7 @@
F72944F52A8424F800246839 /* NCEndToEndMetadataV1.swift in Sources */,
F710D2022405826100A6033D /* NCViewerContextMenu.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 */,
Expand Down Expand Up @@ -4504,6 +4515,7 @@
F70898692EDDB51700EF85BD /* NCSelectOpen+SelectDelegate.swift in Sources */,
F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */,
F7D4BF422CA2E8D800A5E746 /* TOPasscodeSettingsViewController.m in Sources */,
B5C9801E2DACEB5A0041B146 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */,
F7D4BF432CA2E8D800A5E746 /* TOPasscodeCircleImage.m in Sources */,
F78026102E9CFA3700B63436 /* NCTransfersView.swift in Sources */,
F7D4BF442CA2E8D800A5E746 /* TOPasscodeView.m in Sources */,
Expand Down
179 changes: 144 additions & 35 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class NCFiles: NCCollectionViewCommon {
internal var lastScrollTime: TimeInterval = 0
internal var accumulatedScrollDown: CGFloat = 0

internal var syncMetadatasTask: Task<Void, Never>?

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

Expand Down Expand Up @@ -99,6 +101,9 @@ class NCFiles: NCCollectionViewCommon {
super.viewWillAppear(animated)

Task {
let capabilities = await database.getCapabilities(account: self.session.account) ?? NKCapabilities.Capabilities()
await mainNavigationController?.createPlusMenu(session: self.session, capabilities: capabilities)

await self.reloadDataSource()
}
}
Expand Down Expand Up @@ -142,10 +147,53 @@ class NCFiles: NCCollectionViewCommon {

// MARK: - DataSource

override func reloadDataSource() async {
guard !isSearchingMode else {
await super.reloadDataSource()
return
override func reloadDataSource() {
guard !isSearchingMode
else {
return super.reloadDataSource()
}

// Watchdog: this is only a fail safe "dead lock", I don't think the timeout will ever be called but at least nothing gets stuck, if after 5 sec. (which is a long time in this routine), the semaphore is still locked
//
if self.semaphoreReloadDataSource.wait(timeout: .now() + 5) == .timedOut {
self.semaphoreReloadDataSource.signal()
}

var predicate = self.defaultPredicate
let predicateDirectory = NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, self.serverUrl)
let dataSourceMetadatas = self.dataSource.getMetadatas()

if NCKeychain().getPersonalFilesOnly(account: session.account) {
predicate = self.personalFilesOnlyPredicate
}

self.metadataFolder = database.getMetadataFolder(session: session, serverUrl: self.serverUrl)
self.richWorkspaceText = database.getTableDirectory(predicate: predicateDirectory)?.richWorkspace

let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView)
self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView)

if metadatas.isEmpty {
self.semaphoreReloadDataSource.signal()
return super.reloadDataSource()
}

self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) {
self.semaphoreReloadDataSource.signal()
super.reloadDataSource()
}
}

override func getServerData(refresh: Bool = false) async {
await super.getServerData()

defer {
stopGUIGetServerData()
startSyncMetadata(metadatas: self.dataSource.getMetadatas())
}

Task {
await networking.networkingTasks.cancel(identifier: "\(self.serverUrl)_NCFiles")
}

self.metadataFolder = await self.database.getMetadataFolderAsync(session: self.session, serverUrl: self.serverUrl)
Expand Down Expand Up @@ -181,7 +229,7 @@ class NCFiles: NCCollectionViewCommon {

override func getServerData(forced: Bool = false) async {
defer {
stopGUIGetServerData()
restoreDefaultTitle()
startSyncMetadata(metadatas: self.dataSource.getMetadatas())
}

Expand Down Expand Up @@ -240,30 +288,22 @@ class NCFiles: NCCollectionViewCommon {
self.collectionView.reloadData()
}
}
guard resultsReadFile.error == .success,
let metadata = resultsReadFile.metadata else {
return(nil, resultsReadFile.error, reloadRequired)
guard resultsReadFile.error == .success, let metadata = resultsReadFile.metadata else {
return (nil, resultsReadFile.error, false)
}
let e2eEncrypted = metadata.e2eEncrypted
let ocId = metadata.ocId

await self.database.updateDirectoryRichWorkspaceAsync(metadata.richWorkspace, account: resultsReadFile.account, serverUrl: serverUrl)
let tableDirectory = await self.database.getTableDirectoryAsync(ocId: metadata.ocId)

// Verify LivePhoto
//
reloadRequired = await networking.setLivePhoto(account: resultsReadFile.account)
await NCManageDatabase.shared.deleteLivePhotoError()

let shouldSkipUpdate: Bool = (
!forced &&
!refresh &&
tableDirectory?.etag == metadata.etag &&
!metadata.e2eEncrypted &&
!self.dataSource.isEmpty()
)

if shouldSkipUpdate {
return (nil, NKError(), reloadRequired)
return (nil, NKError(), false)
}

startGUIGetServerData()
Expand All @@ -272,40 +312,30 @@ class NCFiles: NCCollectionViewCommon {
let (account, metadataFolder, metadatas, error) = await NCNetworking.shared.readFolderAsync(serverUrl: serverUrl,
account: session.account,
options: options) { task in
Task {
await NCNetworking.shared.networkingTasks.track(identifier: "\(self.serverUrl)_NCFiles", task: task)
}
self.dataSourceTask = task
if self.dataSource.isEmpty() {
self.collectionView.reloadData()
}
}

guard error == .success else {
return(nil, error, reloadRequired)
return (nil, error, false)
}
reloadRequired = true

if let metadataFolder {
self.metadataFolder = metadataFolder.detachedCopy()
self.richWorkspaceText = metadataFolder.richWorkspace
}

//
// E2EE section
//

guard e2eEncrypted,
let metadatas,
!metadatas.isEmpty,
NCPreferences().isEndToEndEnabled(account: account),
guard let metadataFolder,
isDirectoryE2EE,
NCKeychain().isEndToEndEnabled(account: account),
await !NCNetworkingE2EE().isInUpload(account: account, serverUrl: serverUrl) else {
return(metadatas, error, reloadRequired)
return (metadatas, error, true)
}

/// E2EE
let lock = await self.database.getE2ETokenLockAsync(account: account, serverUrl: serverUrl)
if let e2eToken = lock?.e2eToken {
nkLog(tag: self.global.logTagE2EE, message: "Tocken: \(e2eToken)", minimumLogLevel: .verbose)
}
let results = await NCNetworkingE2EE().getMetadata(fileId: metadataFolder.ocId, e2eToken: lock?.e2eToken, account: account)

let results = await NCNetworkingE2EE().getMetadata(fileId: ocId, e2eToken: lock?.e2eToken, account: account)

Expand Down Expand Up @@ -367,6 +397,79 @@ class NCFiles: NCCollectionViewCommon {
}
}

guard error == .success else {
return(nil, error, reloadRequired)
}
reloadRequired = true

if let metadataFolder {
self.metadataFolder = metadataFolder.detachedCopy()
self.richWorkspaceText = metadataFolder.richWorkspace
}

//
// E2EE section
//

guard e2eEncrypted,
let metadatas,
!metadatas.isEmpty,
NCPreferences().isEndToEndEnabled(account: account),
await !NCNetworkingE2EE().isInUpload(account: account, serverUrl: serverUrl) else {
return(metadatas, error, reloadRequired)
}

let lock = await self.database.getE2ETokenLockAsync(account: account, serverUrl: serverUrl)
if let e2eToken = lock?.e2eToken {
nkLog(tag: self.global.logTagE2EE, message: "Tocken: \(e2eToken)", minimumLogLevel: .verbose)
}

let results = await NCNetworkingE2EE().getMetadata(fileId: ocId, e2eToken: lock?.e2eToken, account: account)

nkLog(tag: self.global.logTagE2EE, message: "Get metadata with error: \(results.error.errorCode)")
nkLog(tag: self.global.logTagE2EE, message: "Get metadata with metadata: \(results.e2eMetadata ?? ""), signature: \(results.signature ?? ""), version \(results.version ?? "")", minimumLogLevel: .verbose)

guard results.error == .success,
let e2eMetadata = results.e2eMetadata,
let version = results.version else {

// No metadata fount, re-send it
if results.error.errorCode == NCGlobal.shared.errorResourceNotFound {
NCContentPresenter().showInfo(description: "Metadata not found")
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
if error != .success {
NCContentPresenter().showError(error: error)
}
} else {
// show error
NCContentPresenter().showError(error: results.error)
}

return(metadatas, error, reloadRequired)
}

let errorDecodeMetadata = await NCEndToEndMetadata().decodeMetadata(e2eMetadata, signature: results.signature, serverUrl: serverUrl, session: self.session)
nkLog(debug: "Decode e2ee metadata with error: \(errorDecodeMetadata.errorCode)")

if errorDecodeMetadata == .success {
let capabilities = await NKCapabilities.shared.getCapabilities(for: self.session.account)
if version == "v1", capabilities.e2EEApiVersion == NCGlobal.shared.e2eeVersionV20 {
NCContentPresenter().showInfo(description: "Conversion metadata v1 to v2 required, please wait...")
nkLog(tag: self.global.logTagE2EE, message: "Conversion v1 to v2")
NCActivityIndicator.shared.start()

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
if error != .success {
NCContentPresenter().showError(error: error)
}
NCActivityIndicator.shared.stop()
}
} else {
// Client Diagnostic
await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
NCContentPresenter().showError(error: error)
}

return (metadatas, error, reloadRequired)
}

Expand Down Expand Up @@ -415,6 +518,12 @@ class NCFiles: NCCollectionViewCommon {
navigationController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() as? UINavigationController
}

UIApplication.shared.firstWindow?.rootViewController = navigationController
} else if let account = tblAccount?.account, account != currentAccount {
Task {
await NCAccount().changeAccount(account, userProfile: nil, controller: controller)
}

UIApplication.shared.mainAppWindow?.rootViewController = navigationController
} else if let account = tblAccount?.account, account != currentAccount {
Task {
Expand Down
23 changes: 19 additions & 4 deletions iOSClient/Main/Collection Common/Cell/NCListCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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!
Expand Down Expand Up @@ -141,6 +142,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
imageItem.layer.masksToBounds = true
imageStatus.image = nil
imageFavorite.image = nil
// imageFavoriteBackground.isHidden = true
imageLocal.image = nil
labelTitle.text = ""
labelInfo.text = ""
Expand Down Expand Up @@ -212,17 +214,30 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
}

func selected(_ status: Bool, isEditMode: Bool) {
// E2EE - remove encrypt folder selection
if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), metadata.e2eEncrypted {
imageSelect.isHidden = true
} else {
imageSelect.isHidden = isEditMode ? false : true
}
// guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), !metadata.e2eEncrypted else {
//// guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), !metadata.e2eEncrypted else {
// backgroundView = nil
// separator.isHidden = false
// imageSelect.isHidden = true
// return
// }
if isEditMode {
imageItemLeftConstraint.constant = 45
imageSelect.isHidden = false
// imageSelect.isHidden = false
imageShared.isHidden = true
imageMore.isHidden = true
buttonShared.isHidden = true
buttonMore.isHidden = true
accessibilityCustomActions = nil
} else {
imageItemLeftConstraint.constant = 10
imageSelect.isHidden = true
// imageSelect.isHidden = true
imageShared.isHidden = false
imageMore.isHidden = false
buttonShared.isHidden = false
Expand All @@ -240,7 +255,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
backgroundView = blurEffectView
separator.isHidden = true
} else {
imageSelect.image = NCImageCache.shared.getImageCheckedNo()
imageSelect.image = NCImageCache.shared.getImageCheckedNo()
backgroundView = nil
separator.isHidden = false
}
Expand Down Expand Up @@ -308,7 +323,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
}

}

override func layoutSubviews() {
super.layoutSubviews()
// Keep the shadow path in sync with current bounds
Expand Down
Loading