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

feat: Uid file v3 #1100

Merged
merged 2 commits into from
Jan 30, 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
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class InMemoryFileListViewModel: FileListViewModel {
func removeFiles(_ files: [ProxyFile]) {
try? realm.write {
for file in files {
if let file = realm.object(ofType: File.self, forPrimaryKey: file.id), !file.isInvalidated {
if let file = realm.object(ofType: File.self, forPrimaryKey: file.uid), !file.isInvalidated {
realm.delete(file)
}
}
Expand Down
4 changes: 2 additions & 2 deletions kDrive/UI/Controller/Files/RootMenuViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ class RootMenuViewController: CustomLargeTitleCollectionViewController, SelectSw

configureDataSource()

let rootChildren = driveFileManager.getRealm()
.object(ofType: File.self, forPrimaryKey: DriveFileManager.constants.rootID)?.children
let rootFileUid = File.uid(driveId: driveFileManager.drive.id, fileId: DriveFileManager.constants.rootID)
let rootChildren = driveFileManager.getRealm().object(ofType: File.self, forPrimaryKey: rootFileUid)?.children
rootChildrenObservationToken = rootChildren?.observe { [weak self] changes in
guard let self else { return }
switch changes {
Expand Down
2 changes: 1 addition & 1 deletion kDrive/UI/Controller/Menu/Trash/TrashListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ class MultipleSelectionTrashViewModel: MultipleSelectionFileListViewModel {
guard let realm = try? Realm(configuration: realmConfiguration) else { return }
try? realm.write {
for file in deletedFiles {
if let file = realm.object(ofType: File.self, forPrimaryKey: file.id), !file.isInvalidated {
if let file = realm.object(ofType: File.self, forPrimaryKey: file.uid), !file.isInvalidated {
realm.delete(file)
}
}
Expand Down
6 changes: 5 additions & 1 deletion kDriveCore/Data/Api/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ public protocol AbstractFile {
}

public struct ProxyFile: AbstractFile, Sendable {
public var uid: String {
File.uid(driveId: driveId, fileId: id)
}

public var driveId: Int
public var id: Int
public var isRoot: Bool {
Expand All @@ -130,7 +134,7 @@ public struct ProxyFile: AbstractFile, Sendable {
}

func resolve(using realm: Realm) throws -> File {
guard let file = realm.object(ofType: File.self, forPrimaryKey: id), !file.isInvalidated else {
guard let file = realm.object(ofType: File.self, forPrimaryKey: uid), !file.isInvalidated else {
throw DriveError.errorWithUserInfo(.fileNotFound, info: [.fileId: ErrorUserInfo(intValue: id)])
}
return file
Expand Down
10 changes: 6 additions & 4 deletions kDriveCore/Data/Cache/DriveFileManager+Listing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,22 @@ public extension DriveFileManager {
!alreadyHandledActionIds.contains(fileAction.fileId) else { continue }
alreadyHandledActionIds.insert(fileAction.fileId)

let fileUid = File.uid(driveId: directory.driveId, fileId: fileAction.fileId)

switch fileAction.action {
case .fileDelete, .fileTrash:
removeFileInDatabase(fileId: fileAction.fileId, cascade: true, withTransaction: false, using: realm)
removeFileInDatabase(fileUid: fileUid, cascade: true, withTransaction: false, using: realm)

case .fileMoveOut:
guard let movedOutFile: File = realm.getObject(id: fileAction.fileId),
guard let movedOutFile: File = realm.getObject(id: fileUid),
let oldParent = movedOutFile.parent else { continue }

oldParent.children.remove(movedOutFile)
case .fileMoveIn, .fileRestore, .fileCreate:
keepCacheAttributesForFile(newFile: actionFile, keepProperties: [.standard, .extras], using: realm)
realm.add(actionFile, update: .modified)

if let existingFile: File = realm.getObject(id: fileAction.fileId),
if let existingFile: File = realm.getObject(id: fileUid),
let oldParent = existingFile.parent {
oldParent.children.remove(existingFile)
}
Expand All @@ -100,7 +102,7 @@ public extension DriveFileManager {
.fileColorUpdate, .fileColorDelete,
.fileCategorize, .fileUncategorize:

if let oldFile: File = realm.getObject(id: fileAction.fileId),
if let oldFile: File = realm.getObject(id: fileUid),
oldFile.name != actionFile.name {
try? renameCachedFile(updatedFile: actionFile, oldFile: oldFile)
}
Expand Down
53 changes: 29 additions & 24 deletions kDriveCore/Data/Cache/DriveFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public final class DriveFileManager {
}
return freeze ? root.freeze() : root
} else {
return File(id: DriveFileManager.constants.rootID, name: drive.name)
return File(id: DriveFileManager.constants.rootID, name: drive.name, driveId: drive.id)
}
}

Expand Down Expand Up @@ -468,7 +468,8 @@ public final class DriveFileManager {
let realm = realm ?? getRealm()
realm.refresh()

guard let file = realm.object(ofType: File.self, forPrimaryKey: id), !file.isInvalidated else {
let uid = File.uid(driveId: drive.id, fileId: id)
guard let file = realm.object(ofType: File.self, forPrimaryKey: uid), !file.isInvalidated else {
return nil
}
return freeze ? file.freeze() : file
Expand Down Expand Up @@ -736,7 +737,7 @@ public final class DriveFileManager {
token?.cancel()
if error != nil && error != .taskRescheduled {
// Mark it as not available offline
self.updateFileProperty(fileId: safeFile.id) { file in
self.updateFileProperty(fileUid: safeFile.uid) { file in
file.isAvailableOffline = false
}
}
Expand All @@ -762,14 +763,14 @@ public final class DriveFileManager {
}

public func setFileShareLink(file: ProxyFile, shareLink: ShareLink?) {
updateFileProperty(fileId: file.id) { file in
updateFileProperty(fileUid: file.uid) { file in
file.sharelink = shareLink
file.capabilities.canBecomeSharelink = shareLink == nil
}
}

public func setFileDropBox(file: ProxyFile, dropBox: DropBox?) {
updateFileProperty(fileId: file.id) { file in
updateFileProperty(fileUid: file.uid) { file in
file.dropbox = dropBox
file.capabilities.canBecomeDropbox = dropBox == nil
}
Expand Down Expand Up @@ -882,7 +883,7 @@ public final class DriveFileManager {
let timestamp = try TimeInterval(timestamp ?? file.resolve(using: realm).responseAt)
var page = 1
var moreComing = true
var pagedActions = [Int: FileActivityType]()
var pagedActions = [String: FileActivityType]()
var pagedActivities = ActivitiesResult()
var responseAt = 0
while moreComing {
Expand Down Expand Up @@ -916,7 +917,7 @@ public final class DriveFileManager {
// swiftlint:disable:next cyclomatic_complexity
private func apply(activities: [FileActivity],
to file: File,
pagedActions: inout [Int: FileActivityType],
pagedActions: inout [String: FileActivityType],
timestamp: Int,
using realm: Realm? = nil) -> ActivitiesResult {
var insertedFiles = [File]()
Expand All @@ -926,14 +927,14 @@ public final class DriveFileManager {
realm.refresh()
realm.beginWrite()
for activity in activities {
let fileId = activity.fileId
let fileId = File.uid(driveId: file.driveId, fileId: activity.fileId)
if pagedActions[fileId] == nil {
switch activity.action {
case .fileDelete, .fileTrash:
if let file = realm.object(ofType: File.self, forPrimaryKey: fileId), !file.isInvalidated {
deletedFiles.append(file.freeze())
}
removeFileInDatabase(fileId: fileId, cascade: true, withTransaction: false, using: realm)
removeFileInDatabase(fileUid: fileId, cascade: true, withTransaction: false, using: realm)
if let file = activity.file {
deletedFiles.append(file)
}
Expand Down Expand Up @@ -980,7 +981,7 @@ public final class DriveFileManager {
.fileColorDelete:
if let newFile = activity.file {
if newFile.isTrashed {
removeFileInDatabase(fileId: fileId, cascade: true, withTransaction: false, using: realm)
removeFileInDatabase(fileUid: fileId, cascade: true, withTransaction: false, using: realm)
deletedFiles.append(newFile)
pagedActions[fileId] = .fileDelete
} else {
Expand Down Expand Up @@ -1080,7 +1081,7 @@ public final class DriveFileManager {
let categoryId = category.id
let response = try await apiFetcher.add(category: category, to: file)
if response.result {
updateFileProperty(fileId: file.id) { file in
updateFileProperty(fileUid: file.uid) { file in
let newCategory = FileCategory(categoryId: categoryId, userId: self.drive.userId)
file.categories.append(newCategory)
}
Expand All @@ -1091,7 +1092,7 @@ public final class DriveFileManager {
let categoryId = category.id
let response = try await apiFetcher.add(drive: drive, category: category, to: files)
for fileResponse in response where fileResponse.result {
updateFileProperty(fileId: fileResponse.id) { file in
updateFileProperty(fileUid: File.uid(driveId: drive.id, fileId: fileResponse.id)) { file in
let newCategory = FileCategory(categoryId: categoryId, userId: self.drive.userId)
file.categories.append(newCategory)
}
Expand All @@ -1102,7 +1103,7 @@ public final class DriveFileManager {
let categoryId = category.id
let response = try await apiFetcher.remove(category: category, from: file)
if response {
updateFileProperty(fileId: file.id) { file in
updateFileProperty(fileUid: file.uid) { file in
if let index = file.categories.firstIndex(where: { $0.categoryId == categoryId }) {
file.categories.remove(at: index)
}
Expand All @@ -1114,7 +1115,7 @@ public final class DriveFileManager {
let categoryId = category.id
let response = try await apiFetcher.remove(drive: drive, category: category, from: files)
for fileResponse in response where fileResponse.result {
updateFileProperty(fileId: fileResponse.id) { file in
updateFileProperty(fileUid: File.uid(driveId: drive.id, fileId: fileResponse.id)) { file in
if let index = file.categories.firstIndex(where: { $0.categoryId == categoryId }) {
file.categories.remove(at: index)
}
Expand Down Expand Up @@ -1185,7 +1186,7 @@ public final class DriveFileManager {
response = try await apiFetcher.unfavorite(file: file)
}
if response {
updateFileProperty(fileId: file.id) { file in
updateFileProperty(fileUid: file.uid) { file in
file.isFavorite = favorite
}
}
Expand All @@ -1196,7 +1197,7 @@ public final class DriveFileManager {
backgroundQueue.async { [self] in
let localRealm = getRealm()
let savedFile = try? file.resolve(using: localRealm).freeze()
removeFileInDatabase(fileId: file.id, cascade: true, withTransaction: true, using: localRealm)
removeFileInDatabase(fileUid: file.uid, cascade: true, withTransaction: true, using: localRealm)
if let file = savedFile {
savedFile?.signalChanges(userId: drive.userId)
notifyObserversWith(file: file)
Expand Down Expand Up @@ -1414,18 +1415,18 @@ public final class DriveFileManager {
return Array(children.freeze())
}

func removeFileInDatabase(fileId: Int, cascade: Bool, withTransaction: Bool, using realm: Realm? = nil) {
func removeFileInDatabase(fileUid: String, cascade: Bool, withTransaction: Bool, using realm: Realm? = nil) {
let realm = realm ?? getRealm()
realm.refresh()

if let file = realm.object(ofType: File.self, forPrimaryKey: fileId), !file.isInvalidated {
if let file = realm.object(ofType: File.self, forPrimaryKey: fileUid), !file.isInvalidated {
if fileManager.fileExists(atPath: file.localContainerUrl.path) {
try? fileManager.removeItem(at: file.localContainerUrl) // Check that it was correctly removed?
}

if cascade {
for child in file.children.freeze() where !child.isInvalidated {
removeFileInDatabase(fileId: child.id, cascade: cascade, withTransaction: withTransaction, using: realm)
removeFileInDatabase(fileUid: child.uid, cascade: cascade, withTransaction: withTransaction, using: realm)
}
}
if withTransaction {
Expand Down Expand Up @@ -1460,11 +1461,11 @@ public final class DriveFileManager {
}
}

private func updateFileProperty(fileId: Int, using realm: Realm? = nil, _ block: (File) -> Void) {
private func updateFileProperty(fileUid: String, using realm: Realm? = nil, _ block: (File) -> Void) {
let realm = realm ?? getRealm()
realm.refresh()

if let file = realm.object(ofType: File.self, forPrimaryKey: fileId), !file.isInvalidated {
if let file = realm.object(ofType: File.self, forPrimaryKey: fileUid), !file.isInvalidated {
try? realm.write {
block(file)
}
Expand Down Expand Up @@ -1523,7 +1524,7 @@ public final class DriveFileManager {
let realm = realm ?? getRealm()
realm.refresh()

guard let savedChild = realm.object(ofType: File.self, forPrimaryKey: newFile.id),
guard let savedChild = realm.object(ofType: File.self, forPrimaryKey: newFile.uid),
!savedChild.isInvalidated else { return }
newFile.isAvailableOffline = savedChild.isAvailableOffline
newFile.versionCode = savedChild.versionCode
Expand Down Expand Up @@ -1564,6 +1565,10 @@ public final class DriveFileManager {
if let cachedFile = getCachedFile(id: file.id, freeze: false, using: realm) {
return cachedFile
} else {
if file.isRoot {
file.driveId = drive.id
file.uid = File.uid(driveId: file.driveId, fileId: file.id)
}
keepCacheAttributesForFile(newFile: file, keepProperties: [.all], using: realm)
try? realm.write {
realm.add(file, update: .all)
Expand All @@ -1577,10 +1582,10 @@ public final class DriveFileManager {
}

public func updateColor(directory: File, color: String) async throws -> Bool {
let fileId = directory.id
let fileUid = directory.uid
let result = try await apiFetcher.updateColor(directory: directory.proxify(), color: color)
if result {
updateFileProperty(fileId: fileId) { file in
updateFileProperty(fileUid: fileUid) { file in
file.color = color
}
}
Expand Down
20 changes: 16 additions & 4 deletions kDriveCore/Data/Models/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ public final class File: Object, Codable {

@LazyInjectService var accountManager: AccountManageable

@Persisted(primaryKey: true) public var id = UUID().uuidString.hashValue
@Persisted(primaryKey: true) public var uid = UUID().uuidString
@Persisted public var id: Int
@Persisted public var parentId: Int
/// Drive identifier
@Persisted public var driveId: Int
Expand Down Expand Up @@ -730,13 +731,20 @@ public final class File: Object, Codable {
return ProxyFile(driveId: driveId, id: id)
}

public static func uid(driveId: Int, fileId: Int) -> String {
"\(fileId)\(driveId)"
}

public convenience init(from decoder: Decoder) throws {
self.init()

let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
let id = try container.decode(Int.self, forKey: .id)
self.id = id
parentId = try container.decode(Int.self, forKey: .parentId)
driveId = try container.decode(Int.self, forKey: .driveId)
let driveId = try container.decode(Int.self, forKey: .driveId)
self.driveId = driveId
uid = File.uid(driveId: driveId, fileId: id)
let decodedName = try container.decode(String.self, forKey: .name)
name = decodedName
sortedName = try container.decodeIfPresent(String.self, forKey: .sortedName) ?? decodedName
Expand Down Expand Up @@ -773,10 +781,14 @@ public final class File: Object, Codable {
// primary key is set as default value
}

convenience init(id: Int, name: String) {
convenience init(id: Int, name: String, driveId: Int? = nil) {
self.init()
self.id = id
self.name = name
if let driveId {
self.driveId = driveId
uid = File.uid(driveId: driveId, fileId: id)
}
rawType = "dir"
children = MutableSet<File>()
}
Expand Down
Loading