update archive and delete link mutations to work off of core data

This commit is contained in:
Satindar Dhillon
2022-04-25 11:56:03 -07:00
parent 302410d4b2
commit 9c7a0d2ee9
6 changed files with 65 additions and 80 deletions

View File

@ -163,7 +163,7 @@ import Views
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(
dataService: dataService,
linkId: item.unwrappedID,
objectID: item.objectID,
archived: !item.isArchived
)
}
@ -193,7 +193,7 @@ import Views
if !item.isArchived {
Button {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.unwrappedID, archived: true)
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: true)
}
} label: {
Label("Archive", systemImage: "archivebox")
@ -201,7 +201,7 @@ import Views
} else {
Button {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.unwrappedID, archived: false)
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: false)
}
} label: {
Label("Unarchive", systemImage: "tray.and.arrow.down.fill")
@ -223,7 +223,7 @@ import Views
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, linkId: itemToRemove.unwrappedID)
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}
}
self.itemToRemove = nil
@ -275,7 +275,7 @@ import Views
func contextMenuActionHandler(item: LinkedItem, action: GridCardAction) {
switch action {
case .toggleArchiveStatus:
viewModel.setLinkArchived(dataService: dataService, linkId: item.unwrappedID, archived: !item.isArchived)
viewModel.setLinkArchived(dataService: dataService, objectID: item.objectID, archived: !item.isArchived)
case .delete:
itemToRemove = item
confirmationShown = true
@ -298,7 +298,7 @@ import Views
Button("Remove Link", role: .destructive) {
if let itemToRemove = itemToRemove {
withAnimation {
viewModel.removeLink(dataService: dataService, linkId: itemToRemove.unwrappedID)
viewModel.removeLink(dataService: dataService, objectID: itemToRemove.objectID)
}
}
self.itemToRemove = nil

View File

@ -106,35 +106,19 @@ import Views
.store(in: &subscriptions)
}
func setLinkArchived(dataService: DataService, linkId: String, archived: Bool) {
func setLinkArchived(dataService: DataService, objectID: NSManagedObjectID, archived: Bool) {
// TODO: remove this by making list always fetch from Coredata
if let itemIndex = items.firstIndex(where: { $0.id == linkId }) {
items.remove(at: itemIndex)
}
dataService.archiveLink(itemID: linkId, archived: archived)
guard let itemIndex = items.firstIndex(where: { $0.objectID == objectID }) else { return }
items.remove(at: itemIndex)
dataService.archiveLink(objectID: objectID, archived: archived)
Snackbar.show(message: archived ? "Link archived" : "Link moved to Inbox")
}
func removeLink(dataService: DataService, linkId: String) {
isLoading = true
if let itemIndex = items.firstIndex(where: { $0.id == linkId }) {
items.remove(at: itemIndex)
}
dataService.removeLinkPublisher(itemID: linkId)
.sink(
receiveCompletion: { [weak self] completion in
guard case .failure = completion else { return }
self?.isLoading = false
Snackbar.show(message: "Failed to remove link")
},
receiveValue: { [weak self] _ in
self?.isLoading = false
Snackbar.show(message: "Link removed")
}
)
.store(in: &subscriptions)
func removeLink(dataService: DataService, objectID: NSManagedObjectID) {
guard let itemIndex = items.firstIndex(where: { $0.objectID == objectID }) else { return }
items.remove(at: itemIndex)
Snackbar.show(message: "Link removed")
dataService.removeLink(objectID: objectID)
}
func snoozeUntil(dataService: DataService, linkId: String, until: Date, successMessage: String?) {
@ -164,13 +148,6 @@ import Views
.store(in: &subscriptions)
}
private func updateProgress(itemID: String, progress: Double) {
guard let item = items.first(where: { $0.id == itemID }) else { return }
if let index = items.firstIndex(of: item) {
items[index].readingProgress = progress
}
}
private var searchQuery: String? {
if searchTerm.isEmpty, selectedLabels.isEmpty {
return nil

View File

@ -22,11 +22,15 @@ enum PDFProvider {
}
func handleArchiveAction(dataService: DataService) {
homeFeedViewModel.setLinkArchived(dataService: dataService, linkId: item.unwrappedID, archived: !item.isArchived)
homeFeedViewModel.setLinkArchived(
dataService: dataService,
objectID: item.objectID,
archived: !item.isArchived
)
}
func handleDeleteAction(dataService: DataService) {
homeFeedViewModel.removeLink(dataService: dataService, linkId: item.unwrappedID)
homeFeedViewModel.removeLink(dataService: dataService, objectID: item.objectID)
}
func updateItemReadStatus(dataService: DataService) {

View File

@ -93,7 +93,7 @@ import WebKit
action: {
homeFeedViewModel.setLinkArchived(
dataService: dataService,
linkId: item.unwrappedID,
objectID: item.objectID,
archived: !item.isArchived
)
},
@ -125,7 +125,7 @@ import WebKit
}
.alert("Are you sure?", isPresented: $showDeleteConfirmation) {
Button("Remove Link", role: .destructive) {
homeFeedViewModel.removeLink(dataService: dataService, linkId: item.unwrappedID)
homeFeedViewModel.removeLink(dataService: dataService, objectID: item.objectID)
}
Button("Cancel", role: .cancel, action: {})
}

View File

@ -1,19 +1,22 @@
import CoreData
import Foundation
import Models
import SwiftGraphQL
extension DataService {
public func archiveLink(itemID: String, archived: Bool) {
public func archiveLink(objectID: NSManagedObjectID, archived: Bool) {
// Update CoreData
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: backgroundContext) {
linkedItem.update(inContext: backgroundContext, newIsArchivedValue: archived)
}
backgroundContext.perform { [weak self] in
guard let self = self else { return }
guard let linkedItem = self.backgroundContext.object(with: objectID) as? LinkedItem else { return }
linkedItem.update(inContext: self.backgroundContext, newIsArchivedValue: archived)
// Send update to server
syncLinkArchiveStatus(itemID: itemID, archived: archived)
// Send update to server
self.syncLinkArchiveStatus(itemID: linkedItem.unwrappedID, objectID: objectID, archived: archived)
}
}
func syncLinkArchiveStatus(itemID: String, archived: Bool) {
func syncLinkArchiveStatus(itemID: String, objectID: NSManagedObjectID, archived: Bool) {
enum MutationResult {
case success(linkId: String)
case error(errorCode: Enums.ArchiveLinkErrorCode)
@ -45,7 +48,7 @@ extension DataService {
let syncStatus: ServerSyncStatus = data == nil ? .needsUpdate : .isNSync
context.perform {
guard let linkedItem = LinkedItem.lookup(byID: itemID, inContext: context) else { return }
guard let linkedItem = context.object(with: objectID) as? LinkedItem else { return }
linkedItem.serverSyncStatus = Int64(syncStatus.rawValue)
do {

View File

@ -1,16 +1,23 @@
import Combine
import CoreData
import Foundation
import Models
import SwiftGraphQL
let setBookmarkedArticleSelection = Selection.Article {
try $0.id()
}
extension DataService {
public func removeLink(objectID: NSManagedObjectID) {
// Update CoreData
backgroundContext.perform { [weak self] in
guard let self = self else { return }
guard let linkedItem = self.backgroundContext.object(with: objectID) as? LinkedItem else { return }
linkedItem.remove(inContext: self.backgroundContext)
public extension DataService {
func removeLinkPublisher(
itemID: String
) -> AnyPublisher<String, BasicError> {
// Send update to server
self.syncLinkDeletion(itemID: linkedItem.unwrappedID, objectID: objectID)
}
}
func syncLinkDeletion(itemID: String, objectID _: NSManagedObjectID) {
enum MutationResult {
case success(linkId: String)
case error(errorCode: Enums.SetBookmarkArticleErrorCode)
@ -20,7 +27,9 @@ public extension DataService {
try $0.on(
setBookmarkArticleSuccess: .init {
.success(
linkId: try $0.bookmarkedArticle(selection: setBookmarkedArticleSelection)
linkId: try $0.bookmarkedArticle(selection: Selection.Article {
try $0.id()
})
)
},
setBookmarkArticleError: .init { .error(errorCode: try $0.errorCodes().first ?? .notFound) }
@ -39,32 +48,24 @@ public extension DataService {
let path = appEnvironment.graphqlPath
let headers = networker.defaultHeaders
let context = backgroundContext
return Deferred {
Future { promise in
send(mutation, to: path, headers: headers) { result in
switch result {
case let .success(payload):
if payload.errors != nil {
promise(.failure(.message(messageText: "Error removing link")))
}
send(mutation, to: path, headers: headers) { result in
let data = try? result.get()
let syncStatus: ServerSyncStatus = data == nil ? .needsDeletion : .isNSync
switch payload.data {
case .success:
if let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) {
linkedItem.remove(inContext: self.backgroundContext)
}
promise(.success(itemID))
case .error(errorCode: _):
promise(.failure(.message(messageText: "Error removing link")))
}
case .failure:
promise(.failure(.message(messageText: "Error removing link")))
}
context.perform {
guard let linkedItem = LinkedItem.lookup(byID: itemID, inContext: context) else { return }
linkedItem.serverSyncStatus = Int64(syncStatus.rawValue)
do {
try context.save()
logger.debug("LinkedItem deleted succesfully")
} catch {
context.rollback()
logger.debug("Failed to delete LinkedItem: \(error.localizedDescription)")
}
}
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}