diff --git a/iOS/Master/MasterViewController.swift b/iOS/Master/MasterViewController.swift index cb855e307..4019595b4 100644 --- a/iOS/Master/MasterViewController.swift +++ b/iOS/Master/MasterViewController.swift @@ -268,6 +268,91 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { self.navigationController?.pushViewController(timeline, animated: true) } + + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + guard let node = nodeFor(indexPath) else { + return false + } + return node.representedObject is Feed + } + + override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath { + + // Adjust the index path so that it will never be in the smart feeds area + let destIndexPath: IndexPath = { + if proposedDestinationIndexPath.section == 0 { + return IndexPath(row: 0, section: 1) + } + return proposedDestinationIndexPath + }() + + guard let draggedNode = nodeFor(sourceIndexPath), let destNode = nodeFor(destIndexPath), let parentNode = destNode.parent else { + assertionFailure("This should never happen") + return sourceIndexPath + } + + // If this is a folder and isn't expanded or doesn't have any entries, let the users drop on it + if destNode.representedObject is Folder && (destNode.numberOfChildNodes == 0 || !expandedNodes.contains(destNode)) { + let movementAdjustment = sourceIndexPath > proposedDestinationIndexPath ? 1 : 0 + return IndexPath(row: destIndexPath.row + movementAdjustment, section: destIndexPath.section) + } + + // If we are dragging around in the same container, just return the original source + if parentNode.childNodes.contains(draggedNode) { + return sourceIndexPath + } + + // Suggest to the user the best place to drop the feed + // Revisit if the tree controller can ever be sorted in some other way. + let nodes = parentNode.childNodes + [draggedNode] + var sortedNodes = nodes.sortedAlphabeticallyWithFoldersAtEnd() + let index = sortedNodes.firstIndex(of: draggedNode)! + + if index == 0 { + if parentNode.representedObject is Account { + return IndexPath(row: 0, section: destIndexPath.section) + } else { + return indexPathFor(parentNode)! + } + } else { + sortedNodes.remove(at: sortedNodes.firstIndex(of: draggedNode)!) + let movementAdjustment = sourceIndexPath < proposedDestinationIndexPath ? 1 : 0 + return indexPathFor(sortedNodes[index - movementAdjustment])! + } + + } + + override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { + + let movementAdjustment = sourceIndexPath > destinationIndexPath ? 1 : 0 + let adjustedDestIndexPath = IndexPath(row: destinationIndexPath.row - movementAdjustment, section: destinationIndexPath.section) + + guard let sourceNode = nodeFor(sourceIndexPath), + let destNode = nodeFor(adjustedDestIndexPath), + let feed = sourceNode.representedObject as? Feed else { + return + } + + let destParentNode: Node? = { + if destNode.representedObject is Folder { + return destNode + } else { + if destNode.parent?.representedObject is Folder { + return destNode.parent! + } else { + return nil + } + } + }() + + let account = accountForNode(destNode) + let sourceContainer = sourceNode.parent?.representedObject as? Container + let destinationFolder = destParentNode?.representedObject as? Folder + sourceContainer?.deleteFeed(feed) + account?.addFeed(feed, to: destinationFolder) + account?.structureDidChange() + + } // MARK: Actions @@ -570,7 +655,20 @@ private extension MasterViewController { callback(cell as! MasterTableViewCell, node) } } - + + private func accountForNode(_ node: Node) -> Account? { + if let account = node.representedObject as? Account { + return account + } + if let folder = node.representedObject as? Folder { + return folder.account + } + if let feed = node.representedObject as? Feed { + return feed.account + } + return nil + } + func rebuildShadowTable() { for i in 0..