diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 5c75f1c3c..6aea45a0c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -59,7 +59,7 @@ struct AnimatingCellHeight: AnimatableModifier { viewModel.searchTerm.isEmpty && viewModel.selectedLabels.isEmpty && viewModel.negatedLabels.isEmpty && - viewModel.appliedFilter?.name == "inbox" + viewModel.appliedFilter?.name.lowercased() == "inbox" } var body: some View { @@ -740,7 +740,7 @@ struct AnimatingCellHeight: AnimatableModifier { case .moveToInbox: return AnyView(Button( action: { - // viewModel.addLabel(dataService: dataService, item: item, label: "Inbox", color) + viewModel.moveToFolder(dataService: dataService, item: item, folder: "inbox") }, label: { Label(title: { Text("Move to Library") }, diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 7bb8dfd70..06805fa59 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -258,6 +258,17 @@ import Views dataService.updateLinkReadingProgress(itemID: item.unwrappedID, readingProgress: 0, anchorIndex: 0, force: true) } + func moveToFolder(dataService: DataService, item: Models.LibraryItem, folder: String) { + Task { + do { + try await dataService.moveItem(itemID: item.unwrappedID, folder: folder) + snackbar("Item moved") + } catch { + snackbar("Error performing operation") + } + } + } + func bulkAction(dataService: DataService, action: BulkAction, items: [String]) { if items.count < 1 { snackbar("No items selected") diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/ServerSyncStatus.swift b/apple/OmnivoreKit/Sources/Models/DataModels/ServerSyncStatus.swift index f911cc4de..43aa729f2 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/ServerSyncStatus.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/ServerSyncStatus.swift @@ -6,4 +6,5 @@ public enum ServerSyncStatus: Int { case needsDeletion case needsCreation case needsUpdate + case needsMove } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/MoveItem.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/MoveItem.swift new file mode 100644 index 000000000..b85b30e58 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/MoveItem.swift @@ -0,0 +1,71 @@ +import CoreData +import Foundation +import Models +import SwiftGraphQL + +public extension DataService { + func moveItem(itemID: String, folder: String) async throws { + backgroundContext.performAndWait { + if let linkedItem = Models.LibraryItem.lookup(byID: itemID, inContext: backgroundContext) { + linkedItem.folder = folder + linkedItem.serverSyncStatus = Int64(ServerSyncStatus.needsUpdate.rawValue) + } + do { + try backgroundContext.save() + logger.debug("LinkedItem updated succesfully") + } catch { + backgroundContext.rollback() + logger.debug("Failed to update LinkedItem: \(error.localizedDescription)") + } + } + + try await syncMoveToFolder(itemID: itemID, folder: folder) + } + + func syncMoveToFolder(itemID: String, folder: String) async throws { + enum MutationResult { + case result(itemID: String) + case error(errorMessage: String) + } + + let articleSavingRequestSelection = Selection.ArticleSavingRequest { + try $0.id() + } + + let selection = Selection { + try $0.on( + moveToFolderError: .init { .error(errorMessage: try $0.errorCodes().first?.rawValue ?? "Unknown Error") }, + moveToFolderSuccess: .init { + .result(itemID: try $0.articleSavingRequest(selection: articleSavingRequestSelection)) + } + ) + } + + let mutation = Selection.Mutation { + try $0.moveToFolder( + folder: folder, + id: itemID, + selection: selection + ) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return try await withCheckedThrowingContinuation { continuation in + send(mutation, to: path, headers: headers) { queryResult in + guard let payload = try? queryResult.get() else { + continuation.resume(throwing: BasicError.message(messageText: "network error")) + return + } + + switch payload.data { + case let .result(itemID: _): + continuation.resume() + case let .error(errorMessage: errorMessage): + continuation.resume(throwing: BasicError.message(messageText: errorMessage)) + } + } + } + } +} diff --git a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift index f7417f262..2aa8ba78a 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/OfflineSync.swift @@ -157,6 +157,15 @@ public extension DataService { anchorIndex: Int(item.readingProgressAnchor), force: item.isPDF ) + case .needsMove: + item.serverSyncStatus = Int64(ServerSyncStatus.isSyncing.rawValue) + syncLinkArchiveStatus(itemID: item.unwrappedID, archived: item.isArchived) + syncLinkReadingProgress( + itemID: item.unwrappedID, + readingProgress: item.readingProgress, + anchorIndex: Int(item.readingProgressAnchor), + force: item.isPDF + ) } } } @@ -184,6 +193,9 @@ public extension DataService { } else { highlight.serverSyncStatus = Int64(ServerSyncStatus.isNSync.rawValue) } + case .needsMove: + // Highlights can't be moved + break } } } diff --git a/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift index 64e2432ba..1100da1e4 100644 --- a/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift +++ b/apple/OmnivoreKit/Sources/Views/SyncingIcon.swift @@ -19,22 +19,18 @@ public struct SyncStatusIcon: View { private var cloudIconName: String { switch status { -// case .isNSync: -// return "checkmark.icloud" case .isNSync: return "exclamationmark.icloud" - case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate: + case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate, .needsMove: return "icloud" } } private var cloudIconColor: Color { switch status { -// case .isNSync: -// return .blue case .isNSync: return .red - case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate: + case .isSyncing, .needsCreation, .needsDeletion, .needsUpdate, .needsMove: return .appGrayText } }