Skip to content

Commit

Permalink
chore: Merge branch 'master' into feature-externalLinks
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-coye committed Jan 23, 2025
2 parents 0d04c47 + a37f1b7 commit 589d089
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,28 @@
*/

import CocoaLumberjackSwift
import DifferenceKit
import InfomaniakCore
import InfomaniakDI
import kDriveCore
import RealmSwift
import UIKit

typealias FileDisplayed = CornerCellContainer<File>

final class UploadQueueFoldersViewController: UITableViewController {
var driveFileManager: DriveFileManager!
@LazyInjectService private var accountManager: AccountManageable
@LazyInjectService private var driveInfosManager: DriveInfosManager
@LazyInjectService private var uploadQueue: UploadQueue

@LazyInjectService var accountManager: AccountManageable
@LazyInjectService var driveInfosManager: DriveInfosManager
@LazyInjectService var uploadQueue: UploadQueue
private var frozenUploadingFolders = [FileDisplayed]()
private var notificationToken: NotificationToken?
private var driveFileManager: DriveFileManager!

private var userId: Int {
return driveFileManager.drive.userId
}

private var folders: [File] = []
private var notificationToken: NotificationToken?

override func viewDidLoad() {
super.viewDidLoad()

Expand All @@ -57,53 +60,31 @@ final class UploadQueueFoldersViewController: UITableViewController {

private func setUpObserver() {
guard driveFileManager != nil else { return }
// Get the drives (current + shared with me)
let driveIds = [driveFileManager.drive.id] + driveInfosManager.getDrives(for: userId, sharedWithMe: true)
.map(\.id)

// Observe uploading files
notificationToken = uploadQueue.getUploadingFiles(userId: userId, driveIds: driveIds)
let uploadingFiles = uploadQueue.getUploadingFiles(userId: userId, driveIds: driveIds)
.distinct(by: [\.parentDirectoryId])
.observe(keyPaths: UploadFile.observedProperties, on: .main) { [weak self] change in
guard let self else {
return
}

switch change {
case .initial(let results):
updateFolders(from: results)
tableView.reloadData()
if results.isEmpty {
navigationController?.popViewController(animated: true)
}
case .update(let results, deletions: let deletions, insertions: let insertions, modifications: let modifications):
guard !results.isEmpty else {
navigationController?.popViewController(animated: true)
return
}

// No animation on updating the same lines without changes
if deletions == insertions, modifications.isEmpty {
return
}

tableView.performBatchUpdates {
self.updateFolders(from: results)
// Always apply updates in the following order: deletions, insertions, then modifications.
// Handling insertions before deletions may result in unexpected behavior.
self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
}
case .error(let error):
DDLogError("Realm observer error: \(error)")
}

notificationToken = uploadingFiles.observe(keyPaths: UploadFile.observedProperties, on: .main) { [weak self] change in
guard let self else {
return
}

switch change {
case .initial(let results):
updateFolders(from: results)
case .update(let results, _, _, _):
updateFolders(from: results)
case .error(let error):
DDLogError("Realm observer error: \(error)")
}
}
}

private func updateFolders(from results: Results<UploadFile>) {
let files = results.map { (driveId: $0.driveId, parentId: $0.parentDirectoryId) }
folders = files.compactMap { tuple in
let filesCount = files.count
let folders: [FileDisplayed] = files.enumerated().compactMap { index, tuple in
let parentId = tuple.parentId
let driveId = tuple.driveId

Expand All @@ -120,14 +101,19 @@ final class UploadQueueFoldersViewController: UITableViewController {
return nil
}

return folder
return FileDisplayed(isFirstInList: index == 0,
isLastInList: index == filesCount - 1,
content: folder)
}

// (Pop view controller if nothing to show)
let changeSet = StagedChangeset(source: frozenUploadingFolders, target: folders)
tableView.reload(using: changeSet,
with: UITableView.RowAnimation.automatic,
interrupt: { $0.changeCount > Endpoint.itemsPerPage },
setData: { self.frozenUploadingFolders = $0 })

if folders.isEmpty {
Task { @MainActor in
self.navigationController?.popViewController(animated: true)
}
navigationController?.popViewController(animated: true)
}
}

Expand All @@ -141,15 +127,16 @@ final class UploadQueueFoldersViewController: UITableViewController {
// MARK: - Table view data source

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return folders.count
return frozenUploadingFolders.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(type: UploadFolderTableViewCell.self, for: indexPath)

let folder = folders[indexPath.row]
cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, isLast: indexPath.row == folders.count - 1)
cell.configure(with: folder, drive: driveFileManager.drive)
let folderDisplayed = frozenUploadingFolders[indexPath.row]
cell.initWithPositionAndShadow(isFirst: folderDisplayed.isFirstInList,
isLast: folderDisplayed.isLastInList)
cell.configure(with: folderDisplayed.content, drive: driveFileManager.drive)

return cell
}
Expand All @@ -158,7 +145,7 @@ final class UploadQueueFoldersViewController: UITableViewController {

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let uploadViewController = UploadQueueViewController.instantiate()
uploadViewController.currentDirectory = folders[indexPath.row]
uploadViewController.currentDirectory = frozenUploadingFolders[indexPath.row].content
navigationController?.pushViewController(uploadViewController, animated: true)
}
}
100 changes: 55 additions & 45 deletions kDrive/UI/Controller/Files/Upload/UploadQueueViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
*/

import CocoaLumberjackSwift
import DifferenceKit
import InfomaniakCore
import InfomaniakDI
import kDriveCore
import kDriveResources
import RealmSwift
import UIKit

typealias UploadFileDisplayed = CornerCellContainer<UploadFile>

final class UploadQueueViewController: UIViewController {
@IBOutlet var tableView: UITableView!
@IBOutlet var retryButton: UIBarButtonItem!
Expand All @@ -33,7 +36,7 @@ final class UploadQueueViewController: UIViewController {
@LazyInjectService var uploadQueue: UploadQueue

var currentDirectory: File!
private var uploadingFiles = AnyRealmCollection(List<UploadFile>())
private var liveUploadingFiles = [UploadFileDisplayed]()
private var notificationToken: NotificationToken?

override func viewDidLoad() {
Expand Down Expand Up @@ -70,42 +73,51 @@ final class UploadQueueViewController: UIViewController {
}

notificationToken?.invalidate()
notificationToken = uploadQueue.getUploadingFiles(withParent: currentDirectory.id,
userId: accountManager.currentUserId,
driveId: currentDirectory.driveId)
.observe(keyPaths: UploadFile.observedProperties, on: .main) { [weak self] change in
guard let self else {
return
}

switch change {
case .initial(let results):
uploadingFiles = AnyRealmCollection(results)
tableView.reloadData()
if results.isEmpty {
navigationController?.popViewController(animated: true)
}
case .update(let results, deletions: let deletions, insertions: let insertions, modifications: let modifications):
uploadingFiles = AnyRealmCollection(results)

guard !results.isEmpty else {
navigationController?.popViewController(animated: true)
return
}

tableView.performBatchUpdates {
// Always apply updates in the following order: deletions, insertions, then modifications.
// Handling insertions before deletions may result in unexpected behavior.
self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
}
// Update cell corners
tableView.reloadCorners(insertions: insertions, deletions: deletions, count: results.count)
case .error(let error):
DDLogError("Realm observer error: \(error)")
}

let observedFiles = AnyRealmCollection(uploadQueue.getUploadingFiles(withParent: currentDirectory.id,
userId: accountManager.currentUserId,
driveId: currentDirectory.driveId))
notificationToken = observedFiles.observe(keyPaths: UploadFile.observedProperties, on: .main) { [weak self] change in
guard let self else {
return
}

let newResults: AnyRealmCollection<UploadFile>?
switch change {
case .initial(let results):
newResults = results
case .update(let results, _, _, _):
newResults = results
case .error(let error):
newResults = nil
DDLogError("Realm observer error: \(error)")
}

guard let newResults else {
reloadCollectionViewWith([])
return
}

let wrappedFiles = newResults.enumerated().map { index, item in
UploadFileDisplayed(isFirstInList: index == 0,
isLastInList: index == newResults.count - 1,
content: item)
}

reloadCollectionViewWith(wrappedFiles)
}
}

func reloadCollectionViewWith(_ files: [UploadFileDisplayed]) {
let changeSet = StagedChangeset(source: liveUploadingFiles, target: files)
tableView.reload(using: changeSet,
with: UITableView.RowAnimation.automatic,
interrupt: { $0.changeCount > Endpoint.itemsPerPage },
setData: { self.liveUploadingFiles = $0 })

if files.isEmpty {
navigationController?.popViewController(animated: true)
}
}

@IBAction func cancelButtonPressed(_ sender: UIBarButtonItem) {
Expand All @@ -130,19 +142,17 @@ final class UploadQueueViewController: UIViewController {

extension UploadQueueViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return uploadingFiles.count
return liveUploadingFiles.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(type: UploadTableViewCell.self, for: indexPath)
cell.initWithPositionAndShadow(isFirst: indexPath.row == 0,
isLast: indexPath.row == self.tableView(
tableView,
numberOfRowsInSection: indexPath.section
) - 1)

/// Make sure the file is valid
let file = uploadingFiles[indexPath.row]
let fileWrapper = liveUploadingFiles[indexPath.row]
let file = fileWrapper.content

cell.initWithPositionAndShadow(isFirst: fileWrapper.isFirstInList,
isLast: fileWrapper.isLastInList)

if !file.isInvalidated {
let progress: CGFloat? = (file.progress != nil) ? CGFloat(file.progress!) : nil
cell.configureWith(uploadFile: file, progress: progress)
Expand Down
44 changes: 44 additions & 0 deletions kDriveCore/Data/Models/Upload/CornerCellContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

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 <http://www.gnu.org/licenses/>.
*/

import DifferenceKit
import Foundation

public struct CornerCellContainer<Content: Differentiable>: Differentiable {
public let isFirstInList: Bool
public let isLastInList: Bool
public let content: Content

public init(isFirstInList: Bool, isLastInList: Bool, content: Content) {
self.isFirstInList = isFirstInList
self.isLastInList = isLastInList
self.content = content
}

public var differenceIdentifier: some Hashable {
return content.differenceIdentifier
}

public func isContentEqual(to source: CornerCellContainer) -> Bool {
autoreleasepool {
isFirstInList == source.isFirstInList
&& isLastInList == source.isLastInList
&& content.isContentEqual(to: source.content)
}
}
}
13 changes: 13 additions & 0 deletions kDriveCore/Data/Models/Upload/UploadFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,16 @@ public extension [UploadFile] {
return file
}
}

extension UploadFile: Differentiable {
public var differenceIdentifier: Int {
return id.hashValue
}

public func isContentEqual(to source: UploadFile) -> Bool {
autoreleasepool {
id == source.id
&& error == source.error
}
}
}

0 comments on commit 589d089

Please sign in to comment.