Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements images #3049

Merged
merged 8 commits into from
Sep 11, 2024
Merged
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
2 changes: 1 addition & 1 deletion Brand/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ import Foundation
// Database Realm
//
let databaseName = "nextcloud.realm"
let databaseSchemaVersion: UInt64 = 358
let databaseSchemaVersion: UInt64 = 359
13 changes: 12 additions & 1 deletion iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class tableMetadata: Object {
@objc dynamic var edited: Bool = false
@objc dynamic var etag = ""
@objc dynamic var etagResource = ""
let exifPhotos = List<NCKeyValue>()
@objc dynamic var favorite: Bool = false
@objc dynamic var fileId = ""
@objc dynamic var fileName = ""
Expand Down Expand Up @@ -102,6 +103,7 @@ class tableMetadata: Object {
@objc public var lockTimeOut: Date?
@objc dynamic var path = ""
@objc dynamic var permissions = ""
@objc dynamic var placePhotos: String?
@objc dynamic var quotaUsedBytes: Int64 = 0
@objc dynamic var quotaAvailableBytes: Int64 = 0
@objc dynamic var resourceType = ""
Expand Down Expand Up @@ -353,6 +355,14 @@ extension NCManageDatabase {
metadata.downloadURL = file.downloadURL
metadata.e2eEncrypted = file.e2eEncrypted
metadata.etag = file.etag
for dict in file.exifPhotos {
for (key, value) in dict {
let keyValue = NCKeyValue()
keyValue.key = key
keyValue.value = value
metadata.exifPhotos.append(keyValue)
}
}
metadata.favorite = file.favorite
metadata.fileId = file.fileId
metadata.fileName = file.fileName
Expand All @@ -376,6 +386,7 @@ extension NCManageDatabase {
metadata.lockTimeOut = file.lockTimeOut
metadata.path = file.path
metadata.permissions = file.permissions
metadata.placePhotos = file.placePhotos
metadata.quotaUsedBytes = file.quotaUsedBytes
metadata.quotaAvailableBytes = file.quotaAvailableBytes
metadata.richWorkspace = file.richWorkspace
Expand Down Expand Up @@ -998,7 +1009,7 @@ extension NCManageDatabase {
do {
let realm = try Realm()
realm.refresh()
let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "datePhotosOriginal", ascending: false)
let results = realm.objects(tableMetadata.self).filter(predicate).sorted(byKeyPath: "date", ascending: false)
return Array(results)
} catch let error as NSError {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
Expand Down
7 changes: 6 additions & 1 deletion iOSClient/Data/NCManageDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class NCManageDatabase: NSObject {

override init() {
func migrationSchema(_ migration: Migration, _ oldSchemaVersion: UInt64) {
if oldSchemaVersion < 358 {
if oldSchemaVersion < 359 {
migration.deleteData(forType: tableMetadata.className())
migration.enumerateObjects(ofType: tableDirectory.className()) { _, newObject in
newObject?["etag"] = ""
Expand Down Expand Up @@ -267,3 +267,8 @@ class NCManageDatabase: NSObject {
setAccountUserProfile(account: account2, userProfile: userProfile2)
}
}

class NCKeyValue: Object {
@Persisted var key: String = ""
@Persisted var value: String? = nil
}
2 changes: 1 addition & 1 deletion iOSClient/Media/NCMedia+CollectionViewDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extension NCMedia: UICollectionViewDataSource {
if kind == mediaSectionHeader {
guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() }
header.emptyImage.image = utility.loadImage(named: "photo", colors: [NCBrandColor.shared.getElement(account: session.account)])
if loadingTask != nil || imageCache.createCacheInProgress {
if self.hasRun || imageCache.createCacheInProgress {
header.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
} else {
header.emptyTitle.text = NSLocalizedString("_tutorial_photo_view_", comment: "")
Expand Down
35 changes: 22 additions & 13 deletions iOSClient/Media/NCMedia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class NCMedia: UIViewController {
@IBOutlet weak var menuButton: UIButton!
@IBOutlet weak var gradientView: UIView!

let lockQueue = DispatchQueue(label: "com.nextcloud.mediasearch.lockqueue")
var hasRun: Bool = false

let layout = NCMediaLayout()
var layoutType = NCGlobal.shared.mediaLayoutRatio
var documentPickerViewController: NCDocumentPickerViewController?
Expand All @@ -46,7 +49,6 @@ class NCMedia: UIViewController {
var dataSource = NCMediaDataSource()
var serverUrl = ""
let refreshControl = UIRefreshControl()
var loadingTask: Task<Void, any Error>?
let taskDescriptionRetrievesProperties = "retrievesProperties"
var isTop: Bool = true
var isEditMode = false
Expand Down Expand Up @@ -127,20 +129,27 @@ class NCMedia: UIViewController {
collectionView.refreshControl = refreshControl
refreshControl.action(for: .valueChanged) { _ in
self.reloadDataSource()
self.refreshControl.endRefreshing()
}

NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { _ in
self.layoutType = self.database.getLayoutForView(account: self.session.account, key: NCGlobal.shared.layoutViewMedia, serverUrl: "")?.layout ?? NCGlobal.shared.mediaLayoutRatio
self.reloadDataSource()
self.searchMediaUI(true)
}

NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterClearCache), object: nil, queue: nil) { _ in
self.dataSource.removeAll()
self.searchMediaUI(true)
}
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

navigationController?.setMediaAppreance()
reloadDataSource()
if dataSource.isEmpty() {
reloadDataSource()
}
}

override func viewDidAppear(_ animated: Bool) {
Expand All @@ -152,7 +161,7 @@ class NCMedia: UIViewController {
NotificationCenter.default.addObserver(self, selector: #selector(enterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil)

startTimer()
searchMediaUI()
createMenu()
}

Expand Down Expand Up @@ -204,13 +213,13 @@ class NCMedia: UIViewController {
gradient.frame = gradientView.bounds
}

func startTimer() {
func searchNewMedia() {
// don't start if media chage is in progress
if imageCache.createCacheInProgress {
return
}
timerSearchNewMedia?.invalidate()
timerSearchNewMedia = Timer.scheduledTimer(timeInterval: timeIntervalSearchNewMedia, target: self, selector: #selector(searchMediaUI), userInfo: nil, repeats: false)
timerSearchNewMedia = Timer.scheduledTimer(timeInterval: timeIntervalSearchNewMedia, target: self, selector: #selector(searchMediaUI(_:)), userInfo: nil, repeats: false)
}

// MARK: - NotificationCenter
Expand All @@ -221,15 +230,15 @@ class NCMedia: UIViewController {
let error = userInfo["error"] as? NKError else { return }

dataSource.removeMetadata(ocId)
self.collectionViewReloadData()
collectionView.reloadData()

if error != .success {
NCContentPresenter().showError(error: error)
}
}

@objc func enterForeground(_ notification: NSNotification) {
startTimer()
searchNewMedia()
}

@objc func uploadedFile(_ notification: NSNotification) {
Expand All @@ -239,8 +248,8 @@ class NCMedia: UIViewController {

if error == .success, let metadata = database.getMetadataFromOcId(ocId),
metadata.isImageOrVideo {
self.dataSource.addMetadata(metadata)
self.collectionViewReloadData()
dataSource.addMetadata(metadata)
collectionView.reloadData()
}
}

Expand Down Expand Up @@ -347,13 +356,13 @@ extension NCMedia: UIScrollViewDelegate {
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
if !decelerate {
startTimer()
searchNewMedia()
}
}
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
startTimer()
searchNewMedia()
}

func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
Expand All @@ -371,6 +380,6 @@ extension NCMedia: NCSelectDelegate {
let mediaPath = serverUrl.replacingOccurrences(of: home, with: "")
database.setAccountMediaPath(mediaPath, account: session.account)
reloadDataSource()
startTimer()
searchNewMedia()
}
}
139 changes: 70 additions & 69 deletions iOSClient/Media/NCMediaDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ import NextcloudKit

extension NCMedia {
func reloadDataSource() {
DispatchQueue.global(qos: .userInteractive).async {
DispatchQueue.global().async {
let metadatas = self.database.getResultsMediaMetadatas(predicate: self.getPredicate())
self.dataSource = NCMediaDataSource(metadatas: metadatas)
self.collectionViewReloadData()
DispatchQueue.main.async {
self.collectionView.reloadData()
self.refreshControl.endRefreshing()
self.setTitleDate()
}
}
}

Expand All @@ -42,79 +46,77 @@ extension NCMedia {

// MARK: - Search media

@objc func searchMediaUI() {
guard loadingTask == nil,
!isEditMode,
self.viewIfLoaded?.window != nil,
let visibleCells = self.collectionView?.indexPathsForVisibleItems.sorted(by: { $0.row < $1.row }).compactMap({ self.collectionView?.cellForItem(at: $0) }),
let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
else {
return
}

var lessDate: Date?
var greaterDate: Date?
let firstMetadataDate = dataSource.getMetadatas().first?.date
let lastMetadataDate = dataSource.getMetadatas().last?.date
let countMetadatas = dataSource.getMetadatas().count
@objc func searchMediaUI(_ distant: Bool = false) {
self.lockQueue.sync {
guard !self.hasRun,
!isEditMode,
let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account))
else { return }
self.hasRun = true

var lessDate = Date.distantFuture
var greaterDate = Date.distantPast
let firstMetadataDate = dataSource.getMetadatas().first?.date
let lastMetadataDate = dataSource.getMetadatas().last?.date
let countMetadatas = dataSource.getMetadatas().count
var limit = 300
let options = NKRequestOptions(timeout: 120, taskDescription: self.taskDescriptionRetrievesProperties, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)

if let visibleCells = self.collectionView?.indexPathsForVisibleItems.sorted(by: { $0.row < $1.row }).compactMap({ self.collectionView?.cellForItem(at: $0) }), !distant {

// first date
let firstCellDate = (visibleCells.first as? NCGridMediaCell)?.date
if firstCellDate == firstMetadataDate {
lessDate = Date.distantFuture
} else {
if let date = firstCellDate {
lessDate = Calendar.current.date(byAdding: .second, value: 1, to: date)!
} else {
lessDate = Date.distantFuture
}
}

// first date
let firstCellDate = (visibleCells.first as? NCGridMediaCell)?.date
if firstCellDate == firstMetadataDate {
lessDate = Date.distantFuture
} else {
if let date = firstCellDate {
lessDate = Calendar.current.date(byAdding: .second, value: 1, to: date)!
} else {
lessDate = Date.distantFuture
// last date
let lastCellDate = (visibleCells.last as? NCGridMediaCell)?.date
if lastCellDate == lastMetadataDate {
greaterDate = Date.distantPast
} else {
if let date = lastCellDate {
greaterDate = Calendar.current.date(byAdding: .second, value: -1, to: date)!
} else {
greaterDate = Date.distantPast
}
}
}
}
// last date
let lastCellDate = (visibleCells.last as? NCGridMediaCell)?.date
if lastCellDate == lastMetadataDate {
greaterDate = Date.distantPast
} else {
if let date = lastCellDate {
greaterDate = Calendar.current.date(byAdding: .second, value: -1, to: date)!
} else {
greaterDate = Date.distantPast

if countMetadatas == 0 { self.collectionViewReloadData() }
if self.collectionView.visibleCells.count * 2 > limit {
limit = self.collectionView.visibleCells.count * 2
}
}

if lessDate == Date.distantFuture,
greaterDate == Date.distantPast,
countMetadatas > visibleCells.count {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Media search new media oops. something is bad (distantFuture, distantPast): \(countMetadatas)")
return
}
NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Start searchMedia with lessDate \(lessDate), greaterDate \(greaterDate) with limit \(limit)")

if let lessDate, let greaterDate {
activityIndicator.startAnimating()
loadingTask = Task.detached {
if countMetadatas == 0 {
await self.collectionViewReloadData()
}

var limit = 300
if await self.collectionView.visibleCells.count * 2 > limit {
limit = await self.collectionView.visibleCells.count * 2
}

NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] Start searchMedia with lessDate \(lessDate), greaterDate \(greaterDate) with limit \(limit)")
NextcloudKit.shared.searchMedia(path: tableAccount.mediaPath,
lessDate: lessDate,
greaterDate: greaterDate,
elementDate: "d:getlastmodified/",
limit: limit,
showHiddenFiles: NCKeychain().showHiddenFiles,
account: self.session.account,
options: options) { account, files, _, error in
if error == .success, let files, self.session.account == account {

let options = NKRequestOptions(timeout: 120, taskDescription: self.taskDescriptionRetrievesProperties, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
let results = await NCNetworking.shared.searchMedia(path: tableAccount.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: NCKeychain().showHiddenFiles, account: self.session.account, options: options)

if results.error == .success, let files = results.files {
var predicate = NSPredicate(format: "date > %@ AND date < %@", greaterDate as NSDate, lessDate as NSDate)
predicate = await NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, self.getPredicate(showAll: true)])
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, self.getPredicate(showAll: true)])

self.database.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false) { _, metadatas in
let resultsUpdate = self.database.updateMetadatas(metadatas, predicate: predicate)
let isChanged: Bool = (resultsUpdate.metadatasDifferentCount != 0 || resultsUpdate.metadatasModified != 0)

let metadatas = await self.database.convertFilesToMetadatas(files, useFirstAsMetadataFolder: false).metadatas
let resultsUpdate = self.database.updateMetadatas(metadatas, predicate: predicate)
let isChanged: Bool = (resultsUpdate.metadatasDifferentCount != 0 || resultsUpdate.metadatasModified != 0)
NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] End searchMedia UpdateMetadatas with differentCount \(resultsUpdate.metadatasDifferentCount), modified \(resultsUpdate.metadatasModified)")
NextcloudKit.shared.nkCommonInstance.writeLog("[DEBUG] End searchMedia UpdateMetadatas with differentCount \(resultsUpdate.metadatasDifferentCount), modified \(resultsUpdate.metadatasModified)")

Task { @MainActor in
if lessDate == Date.distantFuture, greaterDate == Date.distantPast, !isChanged, metadatas.count == 0 {
self.dataSource.removeAll()
self.collectionViewReloadData()
Expand All @@ -126,15 +128,13 @@ extension NCMedia {
print("searchMediaUI: nothing")
}
}

} else {
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Media search new media error code \(results.error.errorCode) " + results.error.errorDescription)
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Media search new media error code \(error.errorCode) " + error.errorDescription)
}

Task { @MainActor in
self.loadingTask = nil
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
self.hasRun = false
}
}
}
Expand Down Expand Up @@ -210,6 +210,7 @@ public class NCMediaDataSource: NSObject {

init(metadatas: [tableMetadata]) {
super.init()
self.metadatas.removeAll()
for metadata in metadatas {
appendMetadata(metadata)
}
Expand Down
1 change: 1 addition & 0 deletions iOSClient/NCGlobal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ class NCGlobal: NSObject {
let notificationCenterReloadDataNCShare = "reloadDataNCShare"
let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView"
let notificationCenterReloadAvatar = "reloadAvatar"
let notificationCenterClearCache = "clearCache"

let notificationCenterReloadDataSource = "reloadDataSource"
let notificationCenterReloadDataSourceNetwork = "reloadDataSourceNetwork" // userInfo: withQueryDB
Expand Down
Loading
Loading