2019-08-29 01:06:27 +02:00
|
|
|
//
|
|
|
|
// MasterFeedDataSource.swift
|
|
|
|
// NetNewsWire-iOS
|
|
|
|
//
|
|
|
|
// Created by Maurice Parker on 8/28/19.
|
|
|
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import RSCore
|
|
|
|
import RSTree
|
|
|
|
import Account
|
|
|
|
|
|
|
|
class MasterFeedDataSource<SectionIdentifierType, ItemIdentifierType>: UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable {
|
|
|
|
|
2019-09-01 19:43:07 +02:00
|
|
|
private var coordinator: SceneCoordinator!
|
2019-08-29 01:06:27 +02:00
|
|
|
private var errorHandler: ((Error) -> ())!
|
|
|
|
|
2019-09-01 19:43:07 +02:00
|
|
|
init(coordinator: SceneCoordinator, errorHandler: @escaping (Error) -> (), tableView: UITableView, cellProvider: @escaping UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider) {
|
2019-08-29 01:06:27 +02:00
|
|
|
super.init(tableView: tableView, cellProvider: cellProvider)
|
|
|
|
self.coordinator = coordinator
|
|
|
|
self.errorHandler = errorHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
|
|
|
guard let node = coordinator.nodeFor(indexPath), !(node.representedObject is PseudoFeed) else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
|
|
|
guard let node = coordinator.nodeFor(indexPath) else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return node.representedObject is Feed
|
|
|
|
}
|
|
|
|
|
|
|
|
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
|
|
|
|
|
|
|
|
guard let sourceNode = coordinator.nodeFor(sourceIndexPath), let feed = sourceNode.representedObject as? Feed else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Based on the drop we have to determine a node to start looking for a parent container.
|
|
|
|
let destNode: Node = {
|
|
|
|
if destinationIndexPath.row == 0 {
|
|
|
|
return coordinator.rootNode.childAtIndex(destinationIndexPath.section)!
|
|
|
|
} else {
|
|
|
|
let movementAdjustment = sourceIndexPath > destinationIndexPath ? 1 : 0
|
|
|
|
let adjustedDestIndexPath = IndexPath(row: destinationIndexPath.row - movementAdjustment, section: destinationIndexPath.section)
|
|
|
|
return coordinator.nodeFor(adjustedDestIndexPath)!
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Now we start looking for the parent container
|
|
|
|
let destParentNode: Node? = {
|
|
|
|
if destNode.representedObject is Container {
|
|
|
|
return destNode
|
|
|
|
} else {
|
|
|
|
if destNode.parent?.representedObject is Container {
|
|
|
|
return destNode.parent!
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Move the Feed
|
|
|
|
guard let source = sourceNode.parent?.representedObject as? Container, let destination = destParentNode?.representedObject as? Container else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-09-03 20:27:23 +02:00
|
|
|
if sameAccount(sourceNode, destParentNode!) {
|
|
|
|
moveFeedInAccount(feed: feed, sourceContainer: source, destinationContainer: destination)
|
|
|
|
} else {
|
|
|
|
moveFeedBetweenAccounts(feed: feed, sourceContainer: source, destinationContainer: destination)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private func sameAccount(_ node: Node, _ parentNode: Node) -> Bool {
|
|
|
|
if let accountID = nodeAccountID(node), let parentAccountID = nodeAccountID(parentNode) {
|
|
|
|
if accountID == parentAccountID {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
private func nodeAccount(_ node: Node) -> Account? {
|
|
|
|
if let account = node.representedObject as? Account {
|
|
|
|
return account
|
|
|
|
} else if let folder = node.representedObject as? Folder {
|
|
|
|
return folder.account
|
|
|
|
} else if let feed = node.representedObject as? Feed {
|
|
|
|
return feed.account
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private func nodeAccountID(_ node: Node) -> String? {
|
|
|
|
return nodeAccount(node)?.accountID
|
|
|
|
}
|
|
|
|
|
|
|
|
func moveFeedInAccount(feed: Feed, sourceContainer: Container, destinationContainer: Container) {
|
2019-08-29 01:06:27 +02:00
|
|
|
BatchUpdate.shared.start()
|
2019-09-03 20:27:23 +02:00
|
|
|
sourceContainer.account?.moveFeed(feed, from: sourceContainer, to: destinationContainer) { result in
|
|
|
|
BatchUpdate.shared.end()
|
2019-08-29 01:06:27 +02:00
|
|
|
switch result {
|
|
|
|
case .success:
|
2019-09-03 20:27:23 +02:00
|
|
|
break
|
2019-08-29 01:06:27 +02:00
|
|
|
case .failure(let error):
|
|
|
|
self.errorHandler(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-03 20:27:23 +02:00
|
|
|
func moveFeedBetweenAccounts(feed: Feed, sourceContainer: Container, destinationContainer: Container) {
|
|
|
|
|
|
|
|
if let existingFeed = destinationContainer.account?.existingFeed(withURL: feed.url) {
|
|
|
|
|
|
|
|
BatchUpdate.shared.start()
|
|
|
|
destinationContainer.account?.addFeed(existingFeed, to: destinationContainer) { result in
|
|
|
|
switch result {
|
|
|
|
case .success:
|
|
|
|
sourceContainer.account?.removeFeed(feed, from: sourceContainer) { result in
|
|
|
|
BatchUpdate.shared.end()
|
|
|
|
switch result {
|
|
|
|
case .success:
|
|
|
|
break
|
|
|
|
case .failure(let error):
|
|
|
|
self.errorHandler(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case .failure(let error):
|
|
|
|
BatchUpdate.shared.end()
|
|
|
|
self.errorHandler(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
BatchUpdate.shared.start()
|
|
|
|
destinationContainer.account?.createFeed(url: feed.url, name: feed.editedName, container: destinationContainer) { result in
|
|
|
|
switch result {
|
|
|
|
case .success:
|
|
|
|
sourceContainer.account?.removeFeed(feed, from: sourceContainer) { result in
|
|
|
|
BatchUpdate.shared.end()
|
|
|
|
switch result {
|
|
|
|
case .success:
|
|
|
|
break
|
|
|
|
case .failure(let error):
|
|
|
|
self.errorHandler(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case .failure(let error):
|
|
|
|
BatchUpdate.shared.end()
|
|
|
|
self.errorHandler(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2019-08-29 01:06:27 +02:00
|
|
|
|
|
|
|
}
|