diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift
index fd20c7bde..fa1adca3c 100644
--- a/Frameworks/Account/Account.swift
+++ b/Frameworks/Account/Account.swift
@@ -62,6 +62,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 		return defaultName
 	}()
 	
+	public var isDeleted = false
+	
+	public var account: Account? {
+		return self
+	}
 	public let accountID: String
 	public let type: AccountType
 	public var nameForDisplay: String {
@@ -179,6 +184,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
         }
     }
     
+	public var usesTags: Bool {
+		return delegate.usesTags
+	}
+	
 	var refreshInProgress = false {
 		didSet {
 			if refreshInProgress != oldValue {
@@ -406,16 +415,12 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 		return feed
 	}
 
-	func addFeed(container: Container, feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.addFeed(for: self, to: container, with: feed, completion: completion)
+	public func addFeed(_ feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		delegate.addFeed(for: self, with: feed, to: container, completion: completion)
 	}
 
-	func removeFeed(_ feed: Feed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.removeFeed(for: self, from: container, with: feed, completion: completion)
-	}
-	
-	public func createFeed(url: String, completion: @escaping (Result<Feed, Error>) -> Void) {
-		delegate.createFeed(for: self, url: url, completion: completion)
+	public func createFeed(url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
+		delegate.createFeed(for: self, url: url, name: name, container: container, completion: completion)
 	}
 	
 	func createFeed(with name: String?, url: String, feedID: String, homePageURL: String?) -> Feed {
@@ -429,21 +434,28 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 		
 	}
 	
-	public func deleteFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		feedMetadata[feed.url] = nil
-		delegate.deleteFeed(for: self, with: feed, completion: completion)
+	public func removeFeed(_ feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
+		delegate.removeFeed(for: self, with: feed, from: container, completion: completion)
+	}
+	
+	public func moveFeed(_ feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		delegate.moveFeed(for: self, with: feed, from: from, to: to, completion: completion)
 	}
 	
 	public func renameFeed(_ feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
 		delegate.renameFeed(for: self, with: feed, to: name, completion: completion)
 	}
 	
-	public func restoreFeed(_ feed: Feed, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.restoreFeed(for: self, feed: feed, folder: folder, completion: completion)
+	public func restoreFeed(_ feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		delegate.restoreFeed(for: self, feed: feed, container: container, completion: completion)
 	}
 	
-	public func deleteFolder(_ folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.deleteFolder(for: self, with: folder, completion: completion)
+	public func addFolder(_ name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
+		delegate.addFolder(for: self, name: name, completion: completion)
+	}
+	
+	public func removeFolder(_ folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
+		delegate.removeFolder(for: self, with: folder, completion: completion)
 	}
 	
 	public func renameFolder(_ folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
@@ -454,6 +466,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 		delegate.restoreFolder(for: self, folder: folder, completion: completion)
 	}
 	
+	func clearFeedMetadata(_ feed: Feed) {
+		feedMetadata[feed.url] = nil
+	}
+	
 	func addFolder(_ folder: Folder) {
 		folders!.insert(folder)
 		postChildrenDidChangeNotification()
@@ -692,27 +708,25 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 		return _flattenedFeeds
 	}
 
-	public func removeFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.removeFeed(for: self, from: self, with: feed, completion: completion)
-	}
-	
-	public func addFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		delegate.addFeed(for: self, to: self, with: feed, completion: completion)
-	}
-	
-	func removeFeed(_ feed: Feed) {
+	public func removeFeed(_ feed: Feed) {
 		topLevelFeeds.remove(feed)
 		structureDidChange()
 		postChildrenDidChangeNotification()
 	}
 	
-	func addFeed(_ feed: Feed) {
+	public func addFeed(_ feed: Feed) {
 		topLevelFeeds.insert(feed)
 		structureDidChange()
 		postChildrenDidChangeNotification()
 	}
 
-	func deleteFolder(_ folder: Folder) {
+	func addFeedIfNotInAnyFolder(_ feed: Feed) {
+		if !flattenedFeeds().contains(feed) {
+			addFeed(feed)
+		}
+	}
+	
+	func removeFolder(_ folder: Folder) {
 		folders?.remove(folder)
 		structureDidChange()
 		postChildrenDidChangeNotification()
@@ -785,19 +799,19 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
 
 	@objc func saveToDiskIfNeeded() {
 
-		if dirty {
+		if dirty && !isDeleted {
 			saveToDisk()
 		}
 	}
 
 	@objc func saveFeedMetadataIfNeeded() {
-		if feedMetadataDirty {
+		if feedMetadataDirty && !isDeleted {
 			saveFeedMetadata()
 		}
 	}
 
 	@objc func saveAccountMetadataIfNeeded() {
-		if metadataDirty {
+		if metadataDirty && !isDeleted {
 			saveAccountMetadata()
 		}
 	}
diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift
index 8c349203c..1445e1f1f 100644
--- a/Frameworks/Account/AccountDelegate.swift
+++ b/Frameworks/Account/AccountDelegate.swift
@@ -14,6 +14,7 @@ protocol AccountDelegate {
 
 	// Local account does not; some synced accounts might.
 	var supportsSubFolders: Bool { get }
+	var usesTags: Bool { get }
 	var opmlImportInProgress: Bool { get }
 	
 	var server: String? { get }
@@ -28,17 +29,17 @@ protocol AccountDelegate {
 	
 	func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void)
 	
+	func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void)
 	func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
-	func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
+	func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
 
-	func createFeed(for account: Account, url: String, completion: @escaping (Result<Feed, Error>) -> Void)
+	func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void)
 	func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
-	func deleteFeed(for account: Account, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void)
-	
-	func addFeed(for account: Account, to container: Container, with: Feed, completion: @escaping (Result<Void, Error>) -> Void)
-	func removeFeed(for account: Account, from container: Container, with: Feed, completion: @escaping (Result<Void, Error>) -> Void)
+	func addFeed(for account: Account, with: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void)
+	func removeFeed(for account: Account, with feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void)
+	func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void)
 
-	func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void)
+	func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void)
 	func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
 
 	func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>?
diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift
index 7f72d5dd9..f707ff6c1 100644
--- a/Frameworks/Account/AccountManager.swift
+++ b/Frameworks/Account/AccountManager.swift
@@ -127,6 +127,7 @@ public final class AccountManager: UnreadCountProvider {
 		}
 		
 		accountsDictionary.removeValue(forKey: account.accountID)
+		account.isDeleted = true
 		
 		do {
 			try FileManager.default.removeItem(atPath: account.dataFolder)
diff --git a/Frameworks/Account/Container.swift b/Frameworks/Account/Container.swift
index 78afa189d..bd3092894 100644
--- a/Frameworks/Account/Container.swift
+++ b/Frameworks/Account/Container.swift
@@ -18,6 +18,7 @@ extension Notification.Name {
 
 public protocol Container: class {
 
+	var account: Account? { get }
 	var topLevelFeeds: Set<Feed> { get set }
 	var folders: Set<Folder>? { get set }
 
@@ -27,8 +28,8 @@ public protocol Container: class {
 	func hasChildFolder(with: String) -> Bool
 	func childFolder(with: String) -> Folder?
 
-    func removeFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void)
-	func addFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void)
+    func removeFeed(_ feed: Feed)
+	func addFeed(_ feed: Feed)
 
 	//Recursive — checks subfolders
 	func flattenedFeeds() -> Set<Feed>
diff --git a/Frameworks/Account/FeedFinder/FeedFinder.swift b/Frameworks/Account/FeedFinder/FeedFinder.swift
index 89ef24602..c6c2f660e 100644
--- a/Frameworks/Account/FeedFinder/FeedFinder.swift
+++ b/Frameworks/Account/FeedFinder/FeedFinder.swift
@@ -11,38 +11,54 @@ import RSParser
 import RSWeb
 import RSCore
 
-protocol FeedFinderDelegate: class {
-
-	func feedFinder(_: FeedFinder, didFindFeeds: Set<FeedSpecifier>)
-}
-
 class FeedFinder {
+	
+	static func find(url: URL, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
 
-	private weak var delegate: FeedFinderDelegate?
-	private var feedSpecifiers = [String: FeedSpecifier]()
-	private var didNotifyDelegate = false
-
-	var initialDownloadError: Error?
-	var initialDownloadStatusCode = -1
-
-	init(url: URL, delegate: FeedFinderDelegate) {
-
-		self.delegate = delegate
-
-		DispatchQueue.main.async() { () -> Void in
-
-			self.findFeeds(url)
+		downloadUsingCache(url) { (data, response, error) in
+			
+			if response?.forcedStatusCode == 404 {
+				completion(.failure(AccountError.createErrorNotFound))
+				return
+			}
+			
+			if let error = error {
+				completion(.failure(error))
+				return
+			}
+			
+			guard let data = data, let response = response else {
+				completion(.failure(AccountError.createErrorNotFound))
+				return
+			}
+			
+			if !response.statusIsOK || data.isEmpty {
+				completion(.failure(AccountError.createErrorNotFound))
+				return
+			}
+			
+			if FeedFinder.isFeed(data, url.absoluteString) {
+				let feedSpecifier = FeedSpecifier(title: nil, urlString: url.absoluteString, source: .UserEntered)
+				completion(.success(Set([feedSpecifier])))
+				return
+			}
+			
+			if !FeedFinder.isHTML(data) {
+				completion(.failure(AccountError.createErrorNotFound))
+				return
+			}
+			
+			FeedFinder.findFeedsInHTMLPage(htmlData: data, urlString: url.absoluteString, completion: completion)
+			
 		}
+
 	}
 
-	deinit {
-		notifyDelegateIfNeeded()
-	}
 }
 
 private extension FeedFinder {
 
-	func addFeedSpecifier(_ feedSpecifier: FeedSpecifier) {
+	static func addFeedSpecifier(_ feedSpecifier: FeedSpecifier, feedSpecifiers: inout [String: FeedSpecifier]) {
 
 		// If there’s an existing feed specifier, merge the two so that we have the best data. If one has a title and one doesn’t, use that non-nil title. Use the better source.
 
@@ -55,7 +71,7 @@ private extension FeedFinder {
 		}
 	}
 
-	func findFeedsInHTMLPage(htmlData: Data, urlString: String) {
+	static func findFeedsInHTMLPage(htmlData: Data, urlString: String, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
 
 		// Feeds in the <head> section we automatically assume are feeds.
 		// If there are none from the <head> section,
@@ -63,31 +79,35 @@ private extension FeedFinder {
 		// and added once we determine they are feeds.
 
 		let possibleFeedSpecifiers = possibleFeedsInHTMLPage(htmlData: htmlData, urlString: urlString)
+		var feedSpecifiers = [String: FeedSpecifier]()
 		var feedSpecifiersToDownload = Set<FeedSpecifier>()
 
 		var didFindFeedInHTMLHead = false
 
 		for oneFeedSpecifier in possibleFeedSpecifiers {
 			if oneFeedSpecifier.source == .HTMLHead {
-				addFeedSpecifier(oneFeedSpecifier)
+				addFeedSpecifier(oneFeedSpecifier, feedSpecifiers: &feedSpecifiers)
 				didFindFeedInHTMLHead = true
 			}
 			else {
-				if !feedSpecifiersContainsURLString(oneFeedSpecifier.urlString) {
+				if feedSpecifiers[oneFeedSpecifier.urlString] == nil {
 					feedSpecifiersToDownload.insert(oneFeedSpecifier)
 				}
 			}
 		}
 
-		if didFindFeedInHTMLHead || feedSpecifiersToDownload.isEmpty {
-			stopFinding()
-		}
-		else {
-			downloadFeedSpecifiers(feedSpecifiersToDownload)
+		if didFindFeedInHTMLHead {
+			completion(.success(Set(feedSpecifiers.values)))
+			return
+		} else if feedSpecifiersToDownload.isEmpty {
+			completion(.failure(AccountError.createErrorNotFound))
+			return
+		} else {
+			downloadFeedSpecifiers(feedSpecifiersToDownload, feedSpecifiers: feedSpecifiers, completion: completion)
 		}
 	}
 
-	func possibleFeedsInHTMLPage(htmlData: Data, urlString: String) -> Set<FeedSpecifier> {
+	static func possibleFeedsInHTMLPage(htmlData: Data, urlString: String) -> Set<FeedSpecifier> {
 
 		let parserData = ParserData(url: urlString, data: htmlData)
 		var feedSpecifiers = HTMLFeedFinder(parserData: parserData).feedSpecifiers
@@ -109,105 +129,42 @@ private extension FeedFinder {
 		return feedSpecifiers
 	}
 
-	func feedSpecifiersContainsURLString(_ urlString: String) -> Bool {
-
-		if let _ = feedSpecifiers[urlString] {
-			return true
-		}
-		return false
-	}
-
-	func isHTML(_ data: Data) -> Bool {
-
+	static func isHTML(_ data: Data) -> Bool {
 		return (data as NSData).rs_dataIsProbablyHTML()
 	}
 
-	func findFeeds(_ initialURL: URL) {
+	static func downloadFeedSpecifiers(_ downloadFeedSpecifiers: Set<FeedSpecifier>, feedSpecifiers: [String: FeedSpecifier], completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
 
-		downloadInitialFeed(initialURL)
-	}
+		var resultFeedSpecifiers = feedSpecifiers
+		let group = DispatchGroup()
+		
+		for downloadFeedSpecifier in downloadFeedSpecifiers {
 
-	func downloadInitialFeed(_ initialURL: URL) {
-
-		downloadUsingCache(initialURL) { (data, response, error) in
-
-			self.initialDownloadStatusCode = response?.forcedStatusCode ?? -1
-
-			if let error = error {
-				self.initialDownloadError = error
-				self.stopFinding()
-				return
-			}
-			guard let data = data, let response = response else {
-				self.stopFinding()
-				return
-			}
-
-			if !response.statusIsOK || data.isEmpty {
-				self.stopFinding()
-				return
-			}
-
-			if self.isFeed(data, initialURL.absoluteString) {
-				let feedSpecifier = FeedSpecifier(title: nil, urlString: initialURL.absoluteString, source: .UserEntered)
-				self.addFeedSpecifier(feedSpecifier)
-				self.stopFinding()
-				return
-			}
-
-			if !self.isHTML(data) {
-				self.stopFinding()
-				return
-			}
-
-			self.findFeedsInHTMLPage(htmlData: data, urlString: initialURL.absoluteString)
-		}
-	}
-
-	func downloadFeedSpecifiers(_ feedSpecifiers: Set<FeedSpecifier>) {
-
-		var pendingDownloads = feedSpecifiers
-
-		for oneFeedSpecifier in feedSpecifiers {
-
-			guard let url = URL(string: oneFeedSpecifier.urlString) else {
-				pendingDownloads.remove(oneFeedSpecifier)
+			guard let url = URL(string: downloadFeedSpecifier.urlString) else {
 				continue
 			}
-
+			
+			group.enter()
 			downloadUsingCache(url) { (data, response, error) in
-
-				pendingDownloads.remove(oneFeedSpecifier)
-
 				if let data = data, let response = response, response.statusIsOK, error == nil {
-					if self.isFeed(data, oneFeedSpecifier.urlString) {
-						self.addFeedSpecifier(oneFeedSpecifier)
+					if self.isFeed(data, downloadFeedSpecifier.urlString) {
+						addFeedSpecifier(downloadFeedSpecifier, feedSpecifiers: &resultFeedSpecifiers)
 					}
 				}
-
-				if pendingDownloads.isEmpty {
-					self.stopFinding()
-				}
+				group.leave()
 			}
+			
 		}
-	}
 
-	func stopFinding() {
-
-		notifyDelegateIfNeeded()
-	}
-
-	func notifyDelegateIfNeeded() {
-
-		if !didNotifyDelegate {
-			delegate?.feedFinder(self, didFindFeeds: Set(feedSpecifiers.values))
-			didNotifyDelegate = true
+		group.notify(queue: DispatchQueue.main) {
+			completion(.success(Set(resultFeedSpecifiers.values)))
 		}
+		
 	}
 
-	func isFeed(_ data: Data, _ urlString: String) -> Bool {
-
+	static func isFeed(_ data: Data, _ urlString: String) -> Bool {
 		let parserData = ParserData(url: urlString, data: data)
 		return FeedParser.canParse(parserData)
 	}
+	
 }
diff --git a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift
index 7c4c482c1..a5c2b4466 100644
--- a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift
+++ b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift
@@ -143,25 +143,6 @@ final class FeedbinAPICaller: NSObject {
 		transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
 	}
 	
-	func deleteTag(name: String, completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) {
-		
-		let callURL = feedbinBaseURL.appendingPathComponent("tags.json")
-		let request = URLRequest(url: callURL, credentials: credentials)
-		let payload = FeedbinDeleteTag(name: name)
-		
-		transport.send(request: request, method: HTTPMethod.delete, payload: payload, resultType: [FeedbinTagging].self) { result in
-
-			switch result {
-			case .success(let (_, taggings)):
-				completion(.success(taggings))
-			case .failure(let error):
-				completion(.failure(error))
-			}
-
-		}
-		
-	}
-	
 	func retrieveSubscriptions(completion: @escaping (Result<[FeedbinSubscription]?, Error>) -> Void) {
 		
 		let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json")
@@ -438,13 +419,12 @@ final class FeedbinAPICaller: NSObject {
 	
 	func retrieveEntries(page: String, completion: @escaping (Result<([FeedbinEntry]?, String?), Error>) -> Void) {
 		
-		guard let url = URL(string: page), var callComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
+		guard let url = URL(string: page) else {
 			completion(.success((nil, nil)))
 			return
 		}
 		
-		callComponents.queryItems?.append(URLQueryItem(name: "mode", value: "extended"))
-		let request = URLRequest(url: callComponents.url!, credentials: credentials)
+		let request = URLRequest(url: url, credentials: credentials)
 
 		transport.send(request: request, resultType: [FeedbinEntry].self) { result in
 			
@@ -551,10 +531,11 @@ extension FeedbinAPICaller {
 		}
 		
 		if let lowerBound = link.range(of: "page=")?.upperBound {
-			if let upperBound = link.range(of: "&")?.lowerBound {
+			let partialLink = link[lowerBound..<link.endIndex]
+			if let upperBound = partialLink.range(of: "&")?.lowerBound {
 				return Int(link[lowerBound..<upperBound])
 			}
-			if let upperBound = link.range(of: ">")?.lowerBound {
+			if let upperBound = partialLink.range(of: ">")?.lowerBound {
 				return Int(link[lowerBound..<upperBound])
 			}
 		}
diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift
index a684fa611..9faf38ec1 100644
--- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift
+++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift
@@ -31,6 +31,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
 	private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Feedbin")
 
 	let supportsSubFolders = false
+	let usesTags = true
 	let server: String? = "api.feedbin.com"
 	var opmlImportInProgress = false
 	
@@ -230,6 +231,14 @@ final class FeedbinAccountDelegate: AccountDelegate {
 		
 	}
 	
+	func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
+		if let folder = account.ensureFolder(with: name) {
+			completion(.success(folder))
+		} else {
+			completion(.failure(FeedbinAccountDelegateError.invalidParameter))
+		}
+	}
+
 	func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
 		
 		caller.renameTag(oldName: folder.name ?? "", newName: name) { result in
@@ -249,51 +258,46 @@ final class FeedbinAccountDelegate: AccountDelegate {
 		
 	}
 
-	func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
+	func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
 		
 		// Feedbin uses tags and if at least one feed isn't tagged, then the folder doesn't exist on their system
 		guard folder.hasAtLeastOneFeed() else {
-			account.deleteFolder(folder)
+			account.removeFolder(folder)
 			return
 		}
 		
-		// After we successfully delete at Feedbin, we add all the feeds to the account to save them.  We then
-		// delete the folder.  We then sync the taggings we received on the delete to remove any feeds from
-		// the account that might be in another folder.
-		caller.deleteTag(name: folder.name ?? "") { result in
-			switch result {
-			case .success(let taggings):
-				DispatchQueue.main.sync {
-					BatchUpdate.shared.perform {
-						for feed in folder.topLevelFeeds {
-							account.addFeed(feed)
-							self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
-						}
-						account.deleteFolder(folder)
-					}
-					completion(.success(()))
-				}
-				self.syncTaggings(account, taggings)
-			case .failure(let error):
-				DispatchQueue.main.async {
-					let wrappedError = AccountError.wrappedError(error: error, account: account)
-					completion(.failure(wrappedError))
+		let group = DispatchGroup()
+		
+		for feed in folder.topLevelFeeds {
+			group.enter()
+			removeFeed(for: account, with: feed, from: folder) { result in
+				group.leave()
+				switch result {
+				case .success:
+					break
+				case .failure(let error):
+					os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
 				}
 			}
 		}
 		
+		group.notify(queue: DispatchQueue.main) {
+			account.removeFolder(folder)
+			completion(.success(()))
+		}
+		
 	}
 	
-	func createFeed(for account: Account, url: String, completion: @escaping (Result<Feed, Error>) -> Void) {
+	func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
 		
 		caller.createSubscription(url: url) { result in
 			switch result {
 			case .success(let subResult):
 				switch subResult {
 				case .created(let subscription):
-					self.createFeed(account: account, subscription: subscription, completion: completion)
+					self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion)
 				case .multipleChoice(let choices):
-					self.decideBestFeedChoice(account: account, url: url, choices: choices, completion: completion)
+					self.decideBestFeedChoice(account: account, url: url, name: name, container: container, choices: choices, completion: completion)
 				case .alreadySubscribed:
 					DispatchQueue.main.async {
 						completion(.failure(AccountError.createErrorAlreadySubscribed))
@@ -339,37 +343,31 @@ final class FeedbinAccountDelegate: AccountDelegate {
 		
 	}
 
-	func deleteFeed(for account: Account, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		
-		// This error should never happen
-		guard let subscriptionID = feed.subscriptionID else {
-			completion(.failure(FeedbinAccountDelegateError.invalidParameter))
-			return
+	func removeFeed(for account: Account, with feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
+		if feed.folderRelationship?.count ?? 0 > 1 {
+			deleteTagging(for: account, with: feed, from: container, completion: completion)
+		} else {
+			account.clearFeedMetadata(feed)
+			deleteSubscription(for: account, with: feed, from: container, completion: completion)
 		}
-		
-		caller.deleteSubscription(subscriptionID: subscriptionID) { result in
-			switch result {
-			case .success:
-				DispatchQueue.main.async {
-					account.removeFeed(feed)
-					if let folders = account.folders {
-						for folder in folders {
-							folder.removeFeed(feed)
-						}
-					}
-					completion(.success(()))
-				}
-			case .failure(let error):
-				DispatchQueue.main.async {
-					let wrappedError = AccountError.wrappedError(error: error, account: account)
-					completion(.failure(wrappedError))
+	}
+	
+	func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		if from is Account {
+			addFeed(for: account, with: feed, to: to, completion: completion)
+		} else {
+			deleteTagging(for: account, with: feed, from: from) { result in
+				switch result {
+				case .success:
+					self.addFeed(for: account, with: feed, to: to, completion: completion)
+				case .failure(let error):
+					completion(.failure(error))
 				}
 			}
 		}
-		
 	}
-	
-	func addFeed(for account: Account, to container: Container, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
+
+	func addFeed(for account: Account, with feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
 		
 		if let folder = container as? Folder, let feedID = Int(feed.feedID) {
 			caller.createTagging(feedID: feedID, name: folder.name ?? "") { result in
@@ -377,6 +375,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
 				case .success(let taggingID):
 					DispatchQueue.main.async {
 						self.saveFolderRelationship(for: feed, withFolderName: folder.name ?? "", id: String(taggingID))
+						account.removeFeed(feed)
 						folder.addFeed(feed)
 						completion(.success(()))
 					}
@@ -388,55 +387,24 @@ final class FeedbinAccountDelegate: AccountDelegate {
 				}
 			}
 		} else {
-			if let account = container as? Account {
-				account.addFeed(feed)
-			}
 			DispatchQueue.main.async {
+				if let account = container as? Account {
+					account.addFeedIfNotInAnyFolder(feed)
+				}
 				completion(.success(()))
 			}
 		}
 		
 	}
 	
-	func removeFeed(for account: Account, from container: Container, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-
-		if let folder = container as? Folder, let feedTaggingID = feed.folderRelationship?[folder.name ?? ""] {
-			caller.deleteTagging(taggingID: feedTaggingID) { result in
-				switch result {
-				case .success:
-					DispatchQueue.main.async {
-						folder.removeFeed(feed)
-						completion(.success(()))
-					}
-				case .failure(let error):
-					DispatchQueue.main.async {
-						let wrappedError = AccountError.wrappedError(error: error, account: account)
-						completion(.failure(wrappedError))
-					}
-				}
-			}
-		} else {
-			if let account = container as? Account {
-				account.removeFeed(feed)
-			}
-			completion(.success(()))
-		}
+	func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
 		
-	}
-	
-	func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void) {
-		
-		let editedName = feed.editedName
-		
-		createFeed(for: account, url: feed.url) { result in
+		createFeed(for: account, url: feed.url, name: feed.editedName, container: container) { result in
 			switch result {
-			case .success(let feed):
-				self.processRestoredFeed(for: account, feed: feed, editedName: editedName, folder: folder, completion: completion)
+			case .success:
+				completion(.success(()))
 			case .failure(let error):
-				DispatchQueue.main.async {
-					let wrappedError = AccountError.wrappedError(error: error, account: account)
-					completion(.failure(wrappedError))
-				}
+				completion(.failure(error))
 			}
 		}
 		
@@ -450,7 +418,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
 		for feed in folder.topLevelFeeds {
 			
 			group.enter()
-			addFeed(for: account, to: folder, with: feed) { result in
+			addFeed(for: account, with: feed, to: folder) { result in
 				if account.topLevelFeeds.contains(feed) {
 					account.removeFeed(feed)
 				}
@@ -572,7 +540,7 @@ private extension FeedbinAccountDelegate {
 							account.addFeed(feed)
 							clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
 						}
-						account.deleteFolder(folder)
+						account.removeFolder(folder)
 					}
 				}
 			}
@@ -824,74 +792,6 @@ private extension FeedbinAccountDelegate {
 		
 	}
 	
-	func processRestoredFeed(for account: Account, feed: Feed, editedName: String?, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void) {
-		
-		if let folder = folder {
-			
-			addFeed(for: account, to: folder, with: feed) { result in
-				
-				switch result {
-				case .success:
-					
-					if editedName != nil {
-						DispatchQueue.main.async {
-							account.removeFeed(feed)
-							folder.addFeed(feed)
-						}
-						self.processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion)
-					} else {
-						DispatchQueue.main.async {
-							account.removeFeed(feed)
-							folder.addFeed(feed)
-							completion(.success(()))
-						}
-					}
-					
-				case .failure(let error):
-					DispatchQueue.main.async {
-						completion(.failure(error))
-					}
-				}
-				
-			}
-			
-		} else {
-			
-			DispatchQueue.main.async {
-				account.addFeed(feed)
-			}
-			
-			if editedName != nil {
-				processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion)
-			} else {
-				DispatchQueue.main.async {
-					completion(.success(()))
-				}
-			}
-			
-		}
-		
-	}
-	
-	func processRestoredFeedName(for account: Account, feed: Feed, editedName: String, completion: @escaping (Result<Void, Error>) -> Void) {
-		
-		renameFeed(for: account, with: feed, to: editedName) { result in
-			switch result {
-			case .success:
-				DispatchQueue.main.async {
-					feed.editedName = editedName
-					completion(.success(()))
-				}
-			case .failure(let error):
-				DispatchQueue.main.async {
-					completion(.failure(error))
-				}
-			}
-			
-		}
-		
-	}
-	
 	func clearFolderRelationship(for feed: Feed, withFolderName folderName: String) {
 		if var folderRelationship = feed.folderRelationship {
 			folderRelationship[folderName] = nil
@@ -908,7 +808,7 @@ private extension FeedbinAccountDelegate {
 		}
 	}
 
-	func decideBestFeedChoice(account: Account, url: String, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result<Feed, Error>) -> Void) {
+	func decideBestFeedChoice(account: Account, url: String, name: String?, container: Container, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result<Feed, Error>) -> Void) {
 		
 		let feedSpecifiers: [FeedSpecifier] = choices.map { choice in
 			let source = url == choice.url ? FeedSpecifier.Source.UserEntered : FeedSpecifier.Source.HTMLLink
@@ -918,7 +818,7 @@ private extension FeedbinAccountDelegate {
 
 		if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) {
 			if let bestSubscription = choices.filter({ bestSpecifier.urlString == $0.url }).first {
-				createFeed(for: account, url: bestSubscription.url, completion: completion)
+				createFeed(for: account, url: bestSubscription.url, name: name, container: container, completion: completion)
 			} else {
 				DispatchQueue.main.async {
 					completion(.failure(FeedbinAccountDelegateError.invalidParameter))
@@ -932,44 +832,66 @@ private extension FeedbinAccountDelegate {
 		
 	}
 	
-	func createFeed( account: Account, subscription sub: FeedbinSubscription, completion: @escaping (Result<Feed, Error>) -> Void) {
+	func createFeed( account: Account, subscription sub: FeedbinSubscription, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
 		
+
 		DispatchQueue.main.async {
 			
 			let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL)
 			feed.subscriptionID = String(sub.subscriptionID)
 		
-			// Download the initial articles
-			self.caller.retrieveEntries(feedID: feed.feedID) { result in
-				
+			account.addFeed(feed, to: container) { result in
 				switch result {
-				case .success(let (entries, page)):
-					
-					self.processEntries(account: account, entries: entries) {
-						self.refreshArticles(account, page: page) {
-							self.refreshArticleStatus(for: account) {
-								self.refreshMissingArticles(account) {
-									DispatchQueue.main.async {
-										completion(.success(feed))
-									}
-								}
+				case .success:
+					if let name = name {
+						account.renameFeed(feed, to: name) { result in
+							switch result {
+							case .success:
+								self.initialFeedDownload(account: account, feed: feed, completion: completion)
+							case .failure(let error):
+								completion(.failure(error))
 							}
 						}
+					} else {
+						self.initialFeedDownload(account: account, feed: feed, completion: completion)
 					}
-					
 				case .failure(let error):
-					os_log(.error, log: self.log, "Initial articles download failed: %@.", error.localizedDescription)
-					DispatchQueue.main.async {
-						completion(.success(feed))
-					}
+					completion(.failure(error))
 				}
-				
 			}
-
+			
 		}
 		
 	}
 
+	func initialFeedDownload( account: Account, feed: Feed, completion: @escaping (Result<Feed, Error>) -> Void) {
+		
+		// Download the initial articles
+		self.caller.retrieveEntries(feedID: feed.feedID) { result in
+			
+			switch result {
+			case .success(let (entries, page)):
+				
+				self.processEntries(account: account, entries: entries) {
+					self.refreshArticles(account, page: page) {
+						self.refreshArticleStatus(for: account) {
+							self.refreshMissingArticles(account) {
+								DispatchQueue.main.async {
+									completion(.success(feed))
+								}
+							}
+						}
+					}
+				}
+				
+			case .failure(let error):
+				completion(.failure(error))
+			}
+			
+		}
+ 
+	}
+	
 	func refreshArticles(_ account: Account, completion: @escaping (() -> Void)) {
 
 		os_log(.debug, log: log, "Refreshing articles...")
@@ -1195,5 +1117,63 @@ private extension FeedbinAccountDelegate {
 		}
 		
 	}
+
+	func deleteTagging(for account: Account, with feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
+		
+		if let folder = container as? Folder, let feedTaggingID = feed.folderRelationship?[folder.name ?? ""] {
+			caller.deleteTagging(taggingID: feedTaggingID) { result in
+				switch result {
+				case .success:
+					DispatchQueue.main.async {
+						self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
+						folder.removeFeed(feed)
+						account.addFeedIfNotInAnyFolder(feed)
+						completion(.success(()))
+					}
+				case .failure(let error):
+					DispatchQueue.main.async {
+						let wrappedError = AccountError.wrappedError(error: error, account: account)
+						completion(.failure(wrappedError))
+					}
+				}
+			}
+		} else {
+			if let account = container as? Account {
+				account.removeFeed(feed)
+			}
+			completion(.success(()))
+		}
+		
+	}
+
+	func deleteSubscription(for account: Account, with feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
+		
+		// This error should never happen
+		guard let subscriptionID = feed.subscriptionID else {
+			completion(.failure(FeedbinAccountDelegateError.invalidParameter))
+			return
+		}
+		
+		caller.deleteSubscription(subscriptionID: subscriptionID) { result in
+			switch result {
+			case .success:
+				DispatchQueue.main.async {
+					account.removeFeed(feed)
+					if let folders = account.folders {
+						for folder in folders {
+							folder.removeFeed(feed)
+						}
+					}
+					completion(.success(()))
+				}
+			case .failure(let error):
+				DispatchQueue.main.async {
+					let wrappedError = AccountError.wrappedError(error: error, account: account)
+					completion(.failure(wrappedError))
+				}
+			}
+		}
+		
+	}
 	
 }
diff --git a/Frameworks/Account/Folder.swift b/Frameworks/Account/Folder.swift
index 5bf961fb1..f2ee0551f 100644
--- a/Frameworks/Account/Folder.swift
+++ b/Frameworks/Account/Folder.swift
@@ -95,20 +95,12 @@ public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCoun
 		return topLevelFeeds.contains(feed)
 	}
 
-	public func addFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		account?.addFeed(container: self, feed: feed, completion: completion)
-	}
-	
-	public func removeFeed(_ feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		account?.removeFeed(feed, from: self, completion: completion)
-	}
-	
-	func addFeed(_ feed: Feed) {
+	public func addFeed(_ feed: Feed) {
 		topLevelFeeds.insert(feed)
 		postChildrenDidChangeNotification()
 	}
 	
-	func removeFeed(_ feed: Feed) {
+	public func removeFeed(_ feed: Feed) {
 		topLevelFeeds.remove(feed)
 		postChildrenDidChangeNotification()
 	}
diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift
index eb5fee0d9..36fee4e59 100644
--- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift
+++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift
@@ -19,16 +19,13 @@ public enum LocalAccountDelegateError: String, Error {
 final class LocalAccountDelegate: AccountDelegate {
 	
 	let supportsSubFolders = false
+	let usesTags = false
 	let opmlImportInProgress = false
 	
 	let server: String? = nil
 	var credentials: Credentials?
 	var accountMetadata: AccountMetadata?
 
-	private weak var account: Account?
-	private var feedFinder: FeedFinder?
-	private var createFeedCompletion: ((Result<Feed, Error>) -> Void)?
-	
 	private let refresher = LocalAccountRefresher()
 
 	var refreshProgress: DownloadProgress {
@@ -88,28 +85,50 @@ final class LocalAccountDelegate: AccountDelegate {
 		completion(.success(()))
 
 	}
-
-	func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
-		folder.name = name
-		completion(.success(()))
-	}
 	
-	func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
-		account.deleteFolder(folder)
-		completion(.success(()))
-	}
-	
-	func createFeed(for account: Account, url urlString: String, completion: @escaping (Result<Feed, Error>) -> Void) {
+	func createFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
 		
 		guard let url = URL(string: urlString) else {
 			completion(.failure(LocalAccountDelegateError.invalidParameter))
 			return
 		}
 	
-		self.account = account
-		createFeedCompletion = completion
-		
-		feedFinder = FeedFinder(url: url, delegate: self)
+		FeedFinder.find(url: url) { result in
+			
+			switch result {
+			case .success(let feedSpecifiers):
+				
+				guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
+					let url = URL(string: bestFeedSpecifier.urlString) else {
+						completion(.failure(AccountError.createErrorNotFound))
+						return
+				}
+				
+				if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
+					completion(.failure(AccountError.createErrorAlreadySubscribed))
+					return
+				}
+				
+				let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
+				
+				InitialFeedDownloader.download(url) { parsedFeed in
+					
+					if let parsedFeed = parsedFeed {
+						account.update(feed, with: parsedFeed, {})
+					}
+					
+					feed.editedName = name
+					
+					container.addFeed(feed)
+					completion(.success(feed))
+					
+				}
+				
+			case .failure(let error):
+				completion(.failure(error))
+			}
+			
+		}
 		
 	}
 
@@ -118,55 +137,42 @@ final class LocalAccountDelegate: AccountDelegate {
 		completion(.success(()))
 	}
 
-	func deleteFeed(for account: Account, from container: Container, feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		
-		if let account = container as? Account {
-			account.removeFeed(feed)
-		}
-		if let folder = container as? Folder {
-			folder.removeFeed(feed)
-		}
-		completion(.success(()))
-		
-	}
-
-	func deleteFeed(for account: Account, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		account.removeFeed(feed)
-		if let folders = account.folders {
-			for folder in folders {
-				folder.removeFeed(feed)
-			}
-		}
+	func removeFeed(for account: Account, with feed: Feed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
+		container?.removeFeed(feed)
 		completion(.success(()))
 	}
 	
-	func addFeed(for account: Account, to container: Container, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		if let folder = container as? Folder {
-			folder.addFeed(feed)
-			feed.account = folder.account
-		} else if let account = container as? Account {
-			account.addFeed(feed)
-			feed.account = account
-		}
+	func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		from.removeFeed(feed)
+		to.addFeed(feed)
 		completion(.success(()))
 	}
 	
-	func removeFeed(for account: Account, from container: Container, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void) {
-		if let account = container as? Account {
-			account.removeFeed(feed)
-		}
-		if let folder = container as? Folder {
-			folder.removeFeed(feed)
-		}
+	func addFeed(for account: Account, with feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		container.addFeed(feed)
 		completion(.success(()))
 	}
 	
-	func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void) {
-		if let folder = folder {
-			folder.addFeed(feed)
+	func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
+		container.addFeed(feed)
+		completion(.success(()))
+	}
+	
+	func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
+		if let folder = account.ensureFolder(with: name) {
+			completion(.success(folder))
 		} else {
-			account.addFeed(feed)
+			completion(.failure(FeedbinAccountDelegateError.invalidParameter))
 		}
+	}
+	
+	func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
+		folder.name = name
+		completion(.success(()))
+	}
+	
+	func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
+		account.removeFolder(folder)
 		completion(.success(()))
 	}
 	
@@ -187,42 +193,3 @@ final class LocalAccountDelegate: AccountDelegate {
 	}
 	
 }
-
-extension LocalAccountDelegate: FeedFinderDelegate {
-	
-	// MARK: FeedFinderDelegate
-	
-	public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {
-		
-		if let error = feedFinder.initialDownloadError {
-			if feedFinder.initialDownloadStatusCode == 404 {
-				createFeedCompletion!(.failure(AccountError.createErrorNotFound))
-			} else {
-				createFeedCompletion!(.failure(error))
-			}
-			return
-		}
-		
-		guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
-			let url = URL(string: bestFeedSpecifier.urlString),
-			let account = account else {
-			createFeedCompletion!(.failure(AccountError.createErrorNotFound))
-			return
-		}
-
-		if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
-			createFeedCompletion!(.failure(AccountError.createErrorAlreadySubscribed))
-			return
-		}
-		
-		let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
-		InitialFeedDownloader.download(url) { [weak self] parsedFeed in
-			if let parsedFeed = parsedFeed {
-				account.update(feed, with: parsedFeed, {})
-			}
-			self?.createFeedCompletion!(.success(feed))
-		}
-
-	}
-
-}
diff --git a/Mac/MainWindow/AddFeed/AddFeedController.swift b/Mac/MainWindow/AddFeed/AddFeedController.swift
index 8b1babdbe..7475be1b3 100644
--- a/Mac/MainWindow/AddFeed/AddFeedController.swift
+++ b/Mac/MainWindow/AddFeed/AddFeedController.swift
@@ -52,7 +52,6 @@ class AddFeedController: AddFeedWindowControllerDelegate {
 			return
 		}
 		let account = accountAndFolderSpecifier.account
-		let folder = accountAndFolderSpecifier.folder
 
 		if account.hasFeed(withURL: url.absoluteString) {
 			showAlreadySubscribedError(url.absoluteString)
@@ -61,20 +60,20 @@ class AddFeedController: AddFeedWindowControllerDelegate {
 
 		BatchUpdate.shared.start()
 		
-		account.createFeed(url: url.absoluteString) { [weak self] result in
-			
-			self?.endShowingProgress()
+		account.createFeed(url: url.absoluteString, name: title, container: container) { result in
 			
+			self.endShowingProgress()
+			BatchUpdate.shared.end()
+
 			switch result {
 			case .success(let feed):
-				self?.processFeed(feed, account: account, folder: folder, url: url, title: title)
+				NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
 			case .failure(let error):
-				BatchUpdate.shared.end()
 				switch error {
 				case AccountError.createErrorAlreadySubscribed:
-					self?.showAlreadySubscribedError(url.absoluteString)
+					self.showAlreadySubscribedError(url.absoluteString)
 				case AccountError.createErrorNotFound:
-					self?.showNoFeedsErrorMessage()
+					self.showNoFeedsErrorMessage()
 				default:
 					NSApplication.shared.presentError(error)
 				}
@@ -125,45 +124,6 @@ private extension AddFeedController {
 		}
 	}
 
-	func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) {
-		
-		if let title = title {
-			account.renameFeed(feed, to: title) { result in
-				switch result {
-				case .success:
-					break
-				case .failure(let error):
-					NSApplication.shared.presentError(error)
-				}
-			}
-		}
-		
-		if let folder = folder {
-			folder.addFeed(feed) { result in
-				switch result {
-				case .success:
-					BatchUpdate.shared.end()
-					NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
-				case .failure(let error):
-					BatchUpdate.shared.end()
-					NSApplication.shared.presentError(error)
-				}
-			}
-		} else {
-			account.addFeed(feed) { result in
-				switch result {
-				case .success:
-					BatchUpdate.shared.end()
-					NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
-				case .failure(let error):
-					BatchUpdate.shared.end()
-					NSApplication.shared.presentError(error)
-				}
-			}
-		}
-		
-	}
-	
 	// MARK: Errors
 
 	func showAlreadySubscribedError(_ urlString: String) {
diff --git a/Mac/MainWindow/AddFeed/AddFeedWindowController.swift b/Mac/MainWindow/AddFeed/AddFeedWindowController.swift
index 8c5946f67..5c05b2af5 100644
--- a/Mac/MainWindow/AddFeed/AddFeedWindowController.swift
+++ b/Mac/MainWindow/AddFeed/AddFeedWindowController.swift
@@ -29,7 +29,7 @@ class AddFeedWindowController : NSWindowController {
 
 	private var urlString: String?
 	private var initialName: String?
-	private var initialAccount: Account?
+	private weak var initialAccount: Account?
 	private var initialFolder: Folder?
 	private weak var delegate: AddFeedWindowControllerDelegate?
 	private var folderTreeController: TreeController!
diff --git a/Mac/MainWindow/Sidebar/FolderPasteboardWriter.swift b/Mac/MainWindow/Sidebar/FolderPasteboardWriter.swift
deleted file mode 100644
index b87ade020..000000000
--- a/Mac/MainWindow/Sidebar/FolderPasteboardWriter.swift
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-//  FolderPasteboardWriter.swift
-//  NetNewsWire
-//
-//  Created by Brent Simmons on 2/11/18.
-//  Copyright © 2018 Ranchero Software. All rights reserved.
-//
-
-import AppKit
-import Account
-import RSCore
-
-extension Folder: PasteboardWriterOwner {
-
-	public var pasteboardWriter: NSPasteboardWriting {
-		return FolderPasteboardWriter(folder: self)
-	}
-}
-
-@objc final class FolderPasteboardWriter: NSObject, NSPasteboardWriting {
-
-	private let folder: Folder
-	static let folderUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.folder"
-	static let folderUTIInternalType = NSPasteboard.PasteboardType(rawValue: folderUTIInternal)
-
-	init(folder: Folder) {
-
-		self.folder = folder
-	}
-
-	// MARK: - NSPasteboardWriting
-
-	func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
-
-		return [.string, FolderPasteboardWriter.folderUTIInternalType]
-	}
-
-	func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
-
-		let plist: Any?
-
-		switch type {
-		case .string:
-			plist = folder.nameForDisplay
-		case FolderPasteboardWriter.folderUTIInternalType:
-			plist = internalDictionary()
-		default:
-			plist = nil
-		}
-
-		return plist
-	}
-}
-
-private extension FolderPasteboardWriter {
-
-	private struct Key {
-
-		static let name = "name"
-
-		// Internal
-		static let accountID = "accountID"
-		static let folderID = "folderID"
-	}
-
-	func internalDictionary() -> [String: Any] {
-
-		var d = [String: Any]()
-
-		d[Key.folderID] = folder.folderID
-		if let name = folder.name {
-			d[Key.name] = name
-		}
-		if let accountID = folder.account?.accountID {
-			d[Key.accountID] = accountID
-		}
-
-		return d
-
-	}
-}
-
diff --git a/Mac/MainWindow/Sidebar/PasteboardFolder.swift b/Mac/MainWindow/Sidebar/PasteboardFolder.swift
new file mode 100644
index 000000000..2a7e14eb8
--- /dev/null
+++ b/Mac/MainWindow/Sidebar/PasteboardFolder.swift
@@ -0,0 +1,137 @@
+//
+//  FolderPasteboardWriter.swift
+//  NetNewsWire
+//
+//  Created by Brent Simmons on 2/11/18.
+//  Copyright © 2018 Ranchero Software. All rights reserved.
+//
+
+import AppKit
+import Account
+import RSCore
+
+typealias PasteboardFolderDictionary = [String: String]
+
+struct PasteboardFolder: Hashable {
+	
+	private struct Key {
+		static let name = "name"
+		// Internal
+		static let folderID = "folderID"
+		static let accountID = "accountID"
+	}
+
+	
+	let name: String
+	let folderID: String?
+	let accountID: String?
+	
+	init(name: String, folderID: String?, accountID: String?) {
+		self.name = name
+		self.folderID = folderID
+		self.accountID = accountID
+	}
+	
+	// MARK: - Reading
+	
+	init?(dictionary: PasteboardFolderDictionary) {
+		guard let name = dictionary[Key.name] else {
+			return nil
+		}
+		
+		let folderID = dictionary[Key.folderID]
+		let accountID = dictionary[Key.accountID]
+		
+		self.init(name: name, folderID: folderID, accountID: accountID)
+	}
+	
+	init?(pasteboardItem: NSPasteboardItem) {
+		var pasteboardType: NSPasteboard.PasteboardType?
+		if pasteboardItem.types.contains(FolderPasteboardWriter.folderUTIInternalType) {
+			pasteboardType = FolderPasteboardWriter.folderUTIInternalType
+		}
+
+		if let foundType = pasteboardType {
+			if let folderDictionary = pasteboardItem.propertyList(forType: foundType) as? PasteboardFeedDictionary {
+				self.init(dictionary: folderDictionary)
+				return
+			}
+		}
+		
+		return nil
+	}
+	
+	static func pasteboardFolders(with pasteboard: NSPasteboard) -> Set<PasteboardFolder>? {
+		guard let items = pasteboard.pasteboardItems else {
+			return nil
+		}
+		let folders = items.compactMap { PasteboardFolder(pasteboardItem: $0) }
+		return folders.isEmpty ? nil : Set(folders)
+	}
+	
+	// MARK: - Writing
+	
+	func internalDictionary() -> PasteboardFolderDictionary {
+		var d = PasteboardFeedDictionary()
+		d[PasteboardFolder.Key.name] = name
+		if let folderID = folderID {
+			d[PasteboardFolder.Key.folderID] = folderID
+		}
+		if let accountID = accountID {
+			d[PasteboardFolder.Key.accountID] = accountID
+		}
+		return d
+	}
+}
+
+extension Folder: PasteboardWriterOwner {
+
+	public var pasteboardWriter: NSPasteboardWriting {
+		return FolderPasteboardWriter(folder: self)
+	}
+}
+
+@objc final class FolderPasteboardWriter: NSObject, NSPasteboardWriting {
+
+	private let folder: Folder
+	static let folderUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.folder"
+	static let folderUTIInternalType = NSPasteboard.PasteboardType(rawValue: folderUTIInternal)
+
+	init(folder: Folder) {
+
+		self.folder = folder
+	}
+
+	// MARK: - NSPasteboardWriting
+
+	func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
+
+		return [.string, FolderPasteboardWriter.folderUTIInternalType]
+	}
+
+	func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
+
+		let plist: Any?
+
+		switch type {
+		case .string:
+			plist = folder.nameForDisplay
+		case FolderPasteboardWriter.folderUTIInternalType:
+			plist = internalDictionary
+		default:
+			plist = nil
+		}
+
+		return plist
+	}
+}
+
+private extension FolderPasteboardWriter {
+	var pasteboardFolder: PasteboardFolder {
+		return PasteboardFolder(name: folder.name ?? "", folderID: String(folder.folderID), accountID: folder.account?.accountID)
+	}
+	
+	var internalDictionary: PasteboardFeedDictionary {
+		return pasteboardFolder.internalDictionary()
+	}
+}
diff --git a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift
index fed4cda2d..935b97458 100644
--- a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift
+++ b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift
@@ -54,46 +54,70 @@ import Account
 	}
 
 	func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
-		guard let draggedFeeds = PasteboardFeed.pasteboardFeeds(with: info.draggingPasteboard), !draggedFeeds.isEmpty else {
+		let draggedFolders = PasteboardFolder.pasteboardFolders(with: info.draggingPasteboard)
+		let draggedFeeds = PasteboardFeed.pasteboardFeeds(with: info.draggingPasteboard)
+		if (draggedFolders == nil && draggedFeeds == nil) || (draggedFolders != nil && draggedFeeds != nil)  {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
-
 		let parentNode = nodeForItem(item)
-		let contentsType = draggedFeedContentsType(draggedFeeds)
 
-		switch contentsType {
-		case .singleNonLocal:
-			let draggedNonLocalFeed = singleNonLocalFeed(from: draggedFeeds)!
-			return validateSingleNonLocalFeedDrop(outlineView, draggedNonLocalFeed, parentNode, index)
-		case .singleLocal:
-			let draggedFeed = draggedFeeds.first!
-			return validateSingleLocalFeedDrop(outlineView, draggedFeed, parentNode, index)
-		case .multipleLocal:
-			return validateLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
-		case .multipleNonLocal, .mixed, .empty:
-			return SidebarOutlineDataSource.dragOperationNone
+		if let draggedFolders = draggedFolders {
+			if draggedFolders.count == 1 {
+				return validateLocalFolderDrop(outlineView, draggedFolders.first!, parentNode, index)
+			} else {
+				return validateLocalFoldersDrop(outlineView, draggedFolders, parentNode, index)
+			}
 		}
+		
+		if let draggedFeeds = draggedFeeds {
+			let contentsType = draggedFeedContentsType(draggedFeeds)
+
+			switch contentsType {
+			case .singleNonLocal:
+				let draggedNonLocalFeed = singleNonLocalFeed(from: draggedFeeds)!
+				return validateSingleNonLocalFeedDrop(outlineView, draggedNonLocalFeed, parentNode, index)
+			case .singleLocal:
+				let draggedFeed = draggedFeeds.first!
+				return validateSingleLocalFeedDrop(outlineView, draggedFeed, parentNode, index)
+			case .multipleLocal:
+				return validateLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
+			case .multipleNonLocal, .mixed, .empty:
+				return SidebarOutlineDataSource.dragOperationNone
+			}
+		}
+
+		return SidebarOutlineDataSource.dragOperationNone
 	}
 	
 	func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
-		guard let draggedFeeds = PasteboardFeed.pasteboardFeeds(with: info.draggingPasteboard), !draggedFeeds.isEmpty else {
+		let draggedFolders = PasteboardFolder.pasteboardFolders(with: info.draggingPasteboard)
+		let draggedFeeds = PasteboardFeed.pasteboardFeeds(with: info.draggingPasteboard)
+		if (draggedFolders == nil && draggedFeeds == nil) || (draggedFolders != nil && draggedFeeds != nil)  {
 			return false
 		}
-
 		let parentNode = nodeForItem(item)
-		let contentsType = draggedFeedContentsType(draggedFeeds)
 
-		switch contentsType {
-		case .singleNonLocal:
-			let draggedNonLocalFeed = singleNonLocalFeed(from: draggedFeeds)!
-			return acceptSingleNonLocalFeedDrop(outlineView, draggedNonLocalFeed, parentNode, index)
-		case .singleLocal:
-			return acceptLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
-		case .multipleLocal:
-			return acceptLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
-		case .multipleNonLocal, .mixed, .empty:
-			return false
+		if let draggedFolders = draggedFolders {
+			return acceptLocalFoldersDrop(outlineView, draggedFolders, parentNode, index)
 		}
+		
+		if let draggedFeeds = draggedFeeds {
+			let contentsType = draggedFeedContentsType(draggedFeeds)
+
+			switch contentsType {
+			case .singleNonLocal:
+				let draggedNonLocalFeed = singleNonLocalFeed(from: draggedFeeds)!
+				return acceptSingleNonLocalFeedDrop(outlineView, draggedNonLocalFeed, parentNode, index)
+			case .singleLocal:
+				return acceptLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
+			case .multipleLocal:
+				return acceptLocalFeedsDrop(outlineView, draggedFeeds, parentNode, index)
+			case .multipleNonLocal, .mixed, .empty:
+				return false
+			}
+		}
+		
+		return false
 	}
 }
 
@@ -109,11 +133,10 @@ private extension SidebarOutlineDataSource {
 	}
 
 	func nodeRepresentsDraggableItem(_ node: Node) -> Bool {
-		// Don’t allow PseudoFeed or Folder to be dragged.
+		// Don’t allow PseudoFeed to be dragged.
 		// This will have to be revisited later. For instance,
 		// user-created smart feeds should be draggable, maybe.
-		// And we might allow dragging folders between accounts.
-		return node.representedObject is Feed
+		return node.representedObject is Folder || node.representedObject is Feed
 	}
 
 	// MARK: - Drag and Drop
@@ -173,21 +196,20 @@ private extension SidebarOutlineDataSource {
 		guard let dropTargetNode = ancestorThatCanAcceptLocalFeed(parentNode) else {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
-		if !allParticipantsAreLocalAccounts(dropTargetNode, Set([draggedFeed])) {
-			return SidebarOutlineDataSource.dragOperationNone
-		}
 		if nodeHasChildRepresentingDraggedFeed(dropTargetNode, draggedFeed) {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
-		let dragOperation: NSDragOperation = localFeedsDropOperation(dropTargetNode, Set([draggedFeed]))
+		if violatesTagSpecificBehavior(dropTargetNode, draggedFeed) {
+			return SidebarOutlineDataSource.dragOperationNone
+		}
 		if parentNode == dropTargetNode && index == NSOutlineViewDropOnItemIndex {
-			return dragOperation
+			return localDragOperation()
 		}
 		let updatedIndex = indexWhereDraggedFeedWouldAppear(dropTargetNode, draggedFeed)
 		if parentNode !== dropTargetNode || index != updatedIndex {
 			outlineView.setDropItem(dropTargetNode, dropChildIndex: updatedIndex)
 		}
-		return dragOperation
+		return localDragOperation()
 	}
 
 	func validateLocalFeedsDrop(_ outlineView: NSOutlineView, _ draggedFeeds: Set<PasteboardFeed>, _ parentNode: Node, _ index: Int) -> NSDragOperation {
@@ -195,22 +217,19 @@ private extension SidebarOutlineDataSource {
 		guard let dropTargetNode = ancestorThatCanAcceptLocalFeed(parentNode) else {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
-		if !allParticipantsAreLocalAccounts(dropTargetNode, draggedFeeds) {
+		if nodeHasChildRepresentingAnyDraggedFeed(dropTargetNode, draggedFeeds) {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
-		if nodeHasChildRepresentingAnyDraggedFeed(dropTargetNode, draggedFeeds) {
+		if violatesTagSpecificBehavior(dropTargetNode, draggedFeeds) {
 			return SidebarOutlineDataSource.dragOperationNone
 		}
 		if parentNode !== dropTargetNode || index != NSOutlineViewDropOnItemIndex {
 			outlineView.setDropItem(dropTargetNode, dropChildIndex: NSOutlineViewDropOnItemIndex)
 		}
-		return localFeedsDropOperation(dropTargetNode, draggedFeeds)
+		return localDragOperation()
 	}
 	
-	func localFeedsDropOperation(_ dropTargetNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> NSDragOperation {
-		if allParticipantsAreSameAccount(dropTargetNode, draggedFeeds) {
-			return .move
-		}
+	func localDragOperation() -> NSDragOperation {
 		if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
 			return .copy
 		} else {
@@ -218,7 +237,7 @@ private extension SidebarOutlineDataSource {
 		}
 	}
 
-	private func accountForNode(_ node: Node) -> Account? {
+	func accountForNode(_ node: Node) -> Account? {
 		if let account = node.representedObject as? Account {
 			return account
 		}
@@ -231,7 +250,7 @@ private extension SidebarOutlineDataSource {
 		return nil
 	}
 
-	private func commonAccountsFor(_ nodes: Set<Node>) -> Set<Account> {
+	func commonAccountsFor(_ nodes: Set<Node>) -> Set<Account> {
 
 		var accounts = Set<Account>()
 		for node in nodes {
@@ -243,53 +262,156 @@ private extension SidebarOutlineDataSource {
 		return accounts
 	}
 
-	private func copy(node: Node, to parentNode: Node) {
-		guard let feed = node.representedObject as? Feed else {
+	func accountHasFolderRepresentingAnyDraggedFolders(_ account: Account, _ draggedFolders: Set<PasteboardFolder>) -> Bool {
+		for draggedFolder in draggedFolders {
+			if account.existingFolder(with: draggedFolder.name) != nil {
+				return true
+			}
+		}
+		return false
+	}
+	
+	func validateLocalFolderDrop(_ outlineView: NSOutlineView, _ draggedFolder: PasteboardFolder, _ parentNode: Node, _ index: Int) -> NSDragOperation {
+		guard let dropAccount = parentNode.representedObject as? Account, dropAccount.accountID != draggedFolder.accountID else {
+			return SidebarOutlineDataSource.dragOperationNone
+		}
+		if accountHasFolderRepresentingAnyDraggedFolders(dropAccount, Set([draggedFolder])) {
+			return SidebarOutlineDataSource.dragOperationNone
+		}
+		let updatedIndex = indexWhereDraggedFolderWouldAppear(parentNode, draggedFolder)
+		if index != updatedIndex {
+			outlineView.setDropItem(parentNode, dropChildIndex: updatedIndex)
+		}
+		return localDragOperation()
+	}
+	
+	func validateLocalFoldersDrop(_ outlineView: NSOutlineView, _ draggedFolders: Set<PasteboardFolder>, _ parentNode: Node, _ index: Int) -> NSDragOperation {
+		guard let dropAccount = parentNode.representedObject as? Account else {
+			return SidebarOutlineDataSource.dragOperationNone
+		}
+		if accountHasFolderRepresentingAnyDraggedFolders(dropAccount, draggedFolders) {
+			return SidebarOutlineDataSource.dragOperationNone
+		}
+		for draggedFolder in draggedFolders {
+			if dropAccount.accountID == draggedFolder.accountID {
+				return SidebarOutlineDataSource.dragOperationNone
+			}
+		}
+		if index != NSOutlineViewDropOnItemIndex {
+			outlineView.setDropItem(parentNode, dropChildIndex: NSOutlineViewDropOnItemIndex)
+		}
+		return localDragOperation()
+	}
+	
+	func copyFeedInAccount(node: Node, to parentNode: Node) {
+		guard let feed = node.representedObject as? Feed, let destination = parentNode.representedObject as? Container else {
 			return
 		}
-
-		let destination = parentNode.representedObject as? Container
-
-		BatchUpdate.shared.start()
-		destination?.addFeed(feed) { result in
+		
+		destination.account?.addFeed(feed, to: destination) { result in
 			switch result {
 			case .success:
-				BatchUpdate.shared.end()
 				break
 			case .failure(let error):
-				BatchUpdate.shared.end()
 				NSApplication.shared.presentError(error)
 			}
 		}
 	}
 
-	private func move(node: Node, to parentNode: Node) {
-		guard let feed = node.representedObject as? Feed else {
+	func moveFeedInAccount(node: Node, to parentNode: Node) {
+		guard let feed = node.representedObject as? Feed,
+			let source = node.parent?.representedObject as? Container,
+			let destination = parentNode.representedObject as? Container else {
 			return
 		}
 
-		let source = node.parent?.representedObject as? Container
-		let destination = parentNode.representedObject as? Container
-
 		BatchUpdate.shared.start()
-		source?.removeFeed(feed) { result in
+		source.account?.moveFeed(feed, from: source, to: destination) { result in
 			switch result {
 			case .success:
-				destination?.addFeed(feed) { result in
-					switch result {
-					case .success:
-						BatchUpdate.shared.end()
-						break
-					case .failure(let error):
-						// If the second part of the move failed, try to put the feed back
-						source?.addFeed(feed) { result in}
-						BatchUpdate.shared.end()
-						NSApplication.shared.presentError(error)
-					}
-				}
+				BatchUpdate.shared.end()
 			case .failure(let error):
 				NSApplication.shared.presentError(error)
 			}
+		}
+	}
+
+	func copyFeedBetweenAccounts(node: Node, to parentNode: Node) {
+		guard let feed = node.representedObject as? Feed,
+			let destinationAccount = nodeAccount(parentNode),
+			let destinationContainer = parentNode.representedObject as? Container else {
+			return
+		}
+		
+		if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
+			destinationAccount.addFeed(existingFeed, to: destinationContainer) { result in
+				switch result {
+				case .success:
+					break
+				case .failure(let error):
+					NSApplication.shared.presentError(error)
+				}
+			}
+		} else {
+			destinationAccount.createFeed(url: feed.url, name: feed.editedName, container: destinationContainer) { result in
+				switch result {
+				case .success:
+					break
+				case .failure(let error):
+					NSApplication.shared.presentError(error)
+				}
+			}
+		}
+	}
+
+	func moveFeedBetweenAccounts(node: Node, to parentNode: Node) {
+		guard let feed = node.representedObject as? Feed,
+			let sourceAccount = nodeAccount(node),
+			let sourceContainer = node.parent?.representedObject as? Container,
+			let destinationAccount = nodeAccount(parentNode),
+			let destinationContainer = parentNode.representedObject as? Container else {
+				return
+		}
+		
+		if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
+			
+			BatchUpdate.shared.start()
+			destinationAccount.addFeed(existingFeed, to: destinationContainer) { result in
+				switch result {
+				case .success:
+					sourceAccount.removeFeed(feed, from: sourceContainer) { result in
+						BatchUpdate.shared.end()
+						switch result {
+						case .success:
+							break
+						case .failure(let error):
+							NSApplication.shared.presentError(error)
+						}
+					}
+				case .failure(let error):
+					NSApplication.shared.presentError(error)
+				}
+			}
+			
+		} else {
+			
+			BatchUpdate.shared.start()
+			destinationAccount.createFeed(url: feed.url, name: feed.editedName, container: destinationContainer) { result in
+				switch result {
+				case .success:
+					sourceAccount.removeFeed(feed, from: sourceContainer) { result in
+						BatchUpdate.shared.end()
+						switch result {
+						case .success:
+							break
+						case .failure(let error):
+							NSApplication.shared.presentError(error)
+						}
+					}
+				case .failure(let error):
+					NSApplication.shared.presentError(error)
+				}
+			}
 			
 		}
 	}
@@ -299,24 +421,22 @@ private extension SidebarOutlineDataSource {
 			return false
 		}
 
-		BatchUpdate.shared.perform {
-			
-			draggedNodes.forEach { node in
-				if sameAccount(node, parentNode) {
-					move(node: node, to: parentNode)
-				} else if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
-					copy(node: node, to: parentNode)
+		draggedNodes.forEach { node in
+			if sameAccount(node, parentNode) {
+				if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
+					copyFeedInAccount(node: node, to: parentNode)
 				} else {
-					move(node: node, to: parentNode)
+					moveFeedInAccount(node: node, to: parentNode)
+				}
+			} else {
+				if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
+					copyFeedBetweenAccounts(node: node, to: parentNode)
+				} else {
+					moveFeedBetweenAccounts(node: node, to: parentNode)
 				}
 			}
-			
 		}
 		
-		let allReferencedNodes = draggedNodes.union(Set([parentNode]))
-		let accounts = commonAccountsFor(allReferencedNodes)
-		accounts.forEach { $0.structureDidChange() }
-
 		return true
 	}
 
@@ -352,6 +472,94 @@ private extension SidebarOutlineDataSource {
 		return ancestorThatCanAcceptNonLocalFeed(parentNode)
 	}
 
+	func copyFolderBetweenAccounts(node: Node, to parentNode: Node) {
+		guard let sourceFolder = node.representedObject as? Folder,
+			let destinationAccount = nodeAccount(parentNode) else {
+				return
+		}
+		replicateFolder(sourceFolder, destinationAccount: destinationAccount, completion: {})
+	}
+	
+	func moveFolderBetweenAccounts(node: Node, to parentNode: Node) {
+		guard let sourceFolder = node.representedObject as? Folder,
+			let sourceAccount = nodeAccount(node),
+			let destinationAccount = nodeAccount(parentNode) else {
+				return
+		}
+		
+		BatchUpdate.shared.start()
+		replicateFolder(sourceFolder, destinationAccount: destinationAccount) {
+			sourceAccount.removeFolder(sourceFolder) { result in
+				BatchUpdate.shared.end()
+				switch result {
+				case .success:
+					break
+				case .failure(let error):
+					NSApplication.shared.presentError(error)
+				}
+			}
+		}
+	}
+	
+	func replicateFolder(_ folder: Folder, destinationAccount: Account, completion: @escaping () -> Void) {
+		destinationAccount.addFolder(folder.name ?? "") { result in
+			switch result {
+			case .success(let destinationFolder):
+				let group = DispatchGroup()
+				for feed in folder.topLevelFeeds {
+					if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
+						group.enter()
+						destinationAccount.addFeed(existingFeed, to: destinationFolder) { result in
+							group.leave()
+							switch result {
+							case .success:
+								break
+							case .failure(let error):
+								NSApplication.shared.presentError(error)
+							}
+						}
+					} else {
+						group.enter()
+						destinationAccount.createFeed(url: feed.url, name: feed.editedName, container: destinationFolder) { result in
+							group.leave()
+							switch result {
+							case .success:
+								break
+							case .failure(let error):
+								NSApplication.shared.presentError(error)
+							}
+						}
+					}
+				}
+				group.notify(queue: DispatchQueue.main) {
+					completion()
+				}
+			case .failure(let error):
+				NSApplication.shared.presentError(error)
+				completion()
+			}
+		}
+
+	}
+
+	func acceptLocalFoldersDrop(_ outlineView: NSOutlineView, _ draggedFolders: Set<PasteboardFolder>, _ parentNode: Node, _ index: Int) -> Bool {
+		guard let draggedNodes = draggedNodes else {
+			return false
+		}
+		
+		draggedNodes.forEach { node in
+			if !sameAccount(node, parentNode) {
+				if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
+					copyFolderBetweenAccounts(node: node, to: parentNode)
+				} else {
+					moveFolderBetweenAccounts(node: node, to: parentNode)
+				}
+			}
+		}
+		
+		return true
+	}
+
 	func acceptSingleNonLocalFeedDrop(_ outlineView: NSOutlineView, _ draggedFeed: PasteboardFeed, _ parentNode: Node, _ index: Int) -> Bool {
 		guard nodeIsDropTarget(parentNode), index == NSOutlineViewDropOnItemIndex else {
 			return false
@@ -385,44 +593,6 @@ private extension SidebarOutlineDataSource {
 		return false
 	}
 	
-	func allParticipantsAreLocalAccounts(_ parentNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> Bool {
-		
-		if let account = parentNode.representedObject as? Account {
-			if account.type != .onMyMac {
-				return false
-			}
-		} else if let folder = parentNode.representedObject as? Folder {
-			if folder.account?.type != .onMyMac {
-				return false
-			}
-		} else {
-			return false
-		}
-		
-		for draggedFeed in draggedFeeds {
-			if draggedFeed.accountType != .onMyMac {
-				return false
-			}
-		}
-		
-		return true
-		
-	}
-
-	func allParticipantsAreSameAccount(_ parentNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> Bool {
-		guard let parentAccountID = nodeAccountID(parentNode) else {
-			return false
-		}
-		
-		for draggedFeed in draggedFeeds {
-			if draggedFeed.accountID != parentAccountID {
-				return false
-			}
-		}
-		
-		return true
-	}
-	
 	func sameAccount(_ node: Node, _ parentNode: Node) -> Bool {
 		if let accountID = nodeAccountID(node), let parentAccountID = nodeAccountID(parentNode) {
 			if accountID == parentAccountID {
@@ -432,16 +602,21 @@ private extension SidebarOutlineDataSource {
 		return false
 	}
 	
-	func nodeAccountID(_ node: Node) -> String? {
+	func nodeAccount(_ node: Node) -> Account? {
 		if let account = node.representedObject as? Account {
-			return account.accountID
+			return account
 		} else if let folder = node.representedObject as? Folder {
-			return folder.account?.accountID
+			return folder.account
 		} else if let feed = node.representedObject as? Feed {
-			return feed.account?.accountID
+			return feed.account
 		} else {
 			return nil
 		}
+
+	}
+	
+	func nodeAccountID(_ node: Node) -> String? {
+		return nodeAccount(node)?.accountID
 	}
 	
 	func nodeHasChildRepresentingAnyDraggedFeed(_ parentNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> Bool {
@@ -453,6 +628,29 @@ private extension SidebarOutlineDataSource {
 		return false
 	}
 
+	func violatesTagSpecificBehavior(_ parentNode: Node, _ draggedFeed: PasteboardFeed) -> Bool {
+		return violatesTagSpecificBehavior(parentNode, Set([draggedFeed]))
+	}
+	
+	func violatesTagSpecificBehavior(_ parentNode: Node, _ draggedFeeds: Set<PasteboardFeed>) -> Bool {
+		guard let parentAccount = nodeAccount(parentNode), parentAccount.usesTags else {
+			return false
+		}
+		
+		for draggedFeed in draggedFeeds {
+			if parentAccount.accountID != draggedFeed.accountID {
+				return false
+			}
+		}
+		
+		// Can't copy to the account when using tags
+		if parentNode.representedObject is Account && (NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false) {
+			return true
+		}
+		
+		return false
+	}
+	
 	func indexWhereDraggedFeedWouldAppear(_ parentNode: Node, _ draggedFeed: PasteboardFeed) -> Int {
 		let draggedFeedWrapper = PasteboardFeedObjectWrapper(pasteboardFeed: draggedFeed)
 		let draggedFeedNode = Node(representedObject: draggedFeedWrapper, parent: nil)
@@ -463,6 +661,18 @@ private extension SidebarOutlineDataSource {
 		let index = sortedNodes.firstIndex(of: draggedFeedNode)!
 		return index
 	}
+
+	func indexWhereDraggedFolderWouldAppear(_ parentNode: Node, _ draggedFolder: PasteboardFolder) -> Int {
+		let draggedFolderWrapper = PasteboardFolderObjectWrapper(pasteboardFolder: draggedFolder)
+		let draggedFolderNode = Node(representedObject: draggedFolderWrapper, parent: nil)
+		draggedFolderNode.canHaveChildNodes = true
+		let nodes = parentNode.childNodes + [draggedFolderNode]
+		
+		// Revisit if the tree controller can ever be sorted in some other way.
+		let sortedNodes = nodes.sortedAlphabeticallyWithFoldersAtEnd()
+		let index = sortedNodes.firstIndex(of: draggedFolderNode)!
+		return index
+	}
 }
 
 final class PasteboardFeedObjectWrapper: DisplayNameProvider {
@@ -476,3 +686,15 @@ final class PasteboardFeedObjectWrapper: DisplayNameProvider {
 		self.pasteboardFeed = pasteboardFeed
 	}
 }
+
+final class PasteboardFolderObjectWrapper: DisplayNameProvider {
+	
+	var nameForDisplay: String {
+		return pasteboardFolder.name
+	}
+	let pasteboardFolder: PasteboardFolder
+	
+	init(pasteboardFolder: PasteboardFolder) {
+		self.pasteboardFolder = pasteboardFolder
+	}
+}
diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift
index cb890af36..c206be8aa 100644
--- a/Mac/MainWindow/Sidebar/SidebarViewController.swift
+++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift
@@ -60,6 +60,7 @@ protocol SidebarDelegate: class {
 		NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil)
 		NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
 		NotificationCenter.default.addObserver(self, selector: #selector(userDidRequestSidebarSelection(_:)), name: .UserDidRequestSidebarSelection, object: nil)
+		NotificationCenter.default.addObserver(self, selector: #selector(calendarDayChanged(_:)), name: .NSCalendarDayChanged, object: nil)
 
 		outlineView.reloadData()
 
@@ -165,6 +166,12 @@ protocol SidebarDelegate: class {
 		revealAndSelectRepresentedObject(feed as AnyObject)
 	}
 	
+	@objc func calendarDayChanged(_ note: Notification) {
+		DispatchQueue.main.async {
+			SmartFeedsController.shared.todayFeed.fetchUnreadCounts()
+		}
+	}
+	
 	// MARK: - Actions
 
 	@IBAction func delete(_ sender: AnyObject?) {
diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift
index 1aa349a86..3a2ca3ea9 100644
--- a/Mac/MainWindow/Timeline/TimelineViewController.swift
+++ b/Mac/MainWindow/Timeline/TimelineViewController.swift
@@ -147,6 +147,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner {
 			NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil)
 			NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil)
 			NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
+			NotificationCenter.default.addObserver(self, selector: #selector(calendarDayChanged(_:)), name: .NSCalendarDayChanged, object: nil)
 
 				didRegisterForNotifications = true
 		}
@@ -511,6 +512,14 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner {
 		self.fontSize = AppDefaults.timelineFontSize
 		self.sortDirection = AppDefaults.timelineSortDirection
 	}
+	
+	@objc func calendarDayChanged(_ note: Notification) {
+		if representedObjectsContainsTodayFeed() {
+			DispatchQueue.main.async { [weak self] in
+				self?.fetchArticles()
+			}
+		}
+	}
 
 	// MARK: - Reloading Data
 
@@ -966,6 +975,10 @@ private extension TimelineViewController {
 		return representedObjects?.contains(where: { $0 is PseudoFeed}) ?? false
 	}
 
+	func representedObjectsContainsTodayFeed() -> Bool {
+		return representedObjects?.contains(where: { $0 === SmartFeedsController.shared.todayFeed }) ?? false
+	}
+	
 	func representedObjectsContainsAnyFeed(_ feeds: Set<Feed>) -> Bool {
 
 		// Return true if there’s a match or if a folder contains (recursively) one of feeds
diff --git a/Mac/Scriptability/Account+Scriptability.swift b/Mac/Scriptability/Account+Scriptability.swift
index 726d03dc5..88cf8eb3a 100644
--- a/Mac/Scriptability/Account+Scriptability.swift
+++ b/Mac/Scriptability/Account+Scriptability.swift
@@ -50,12 +50,16 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
 	func deleteElement(_ element:ScriptingObject) {
 		if let scriptableFolder = element as? ScriptableFolder {
 			BatchUpdate.shared.perform {
-				account.deleteFolder(scriptableFolder.folder) { result in
+				account.removeFolder(scriptableFolder.folder) { result in
 				}
 			}
 		} else if let scriptableFeed = element as? ScriptableFeed {
 			BatchUpdate.shared.perform {
-				account.deleteFeed(scriptableFeed.feed) { result in
+				var container: Container? = nil
+				if let scriptableFolder = scriptableFeed.container as? ScriptableFolder {
+					container = scriptableFolder.folder
+				}
+				account.removeFeed(scriptableFeed.feed, from: container) { result in
 				}
 			}
 		}
diff --git a/Mac/Scriptability/Feed+Scriptability.swift b/Mac/Scriptability/Feed+Scriptability.swift
index db385b312..42fefde87 100644
--- a/Mac/Scriptability/Feed+Scriptability.swift
+++ b/Mac/Scriptability/Feed+Scriptability.swift
@@ -91,7 +91,9 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
         if let existingFeed = account.existingFeed(withURL:url) {
             return self.scriptableFeed(existingFeed, account:account, folder:folder)
         }
-    
+		
+		let container: Container = folder != nil ? folder! : account
+		
         // at this point, we need to download the feed and parse it.
         // RS Parser does the callback for the download on the main thread (which it probably shouldn't?)
         // because we can't wait here (on the main thread, maybe) for the callback, we have to return from this function
@@ -100,27 +102,12 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
         // suspendExecution().  When we get the callback, we can supply the event result and call resumeExecution()
         command.suspendExecution()
         
-		account.createFeed(url: url) { result in
+		account.createFeed(url: url, name: titleFromArgs, container: container) { result in
 			switch result {
 			case .success(let feed):
-
-				if let editedName = titleFromArgs {
-					account.renameFeed(feed, to: editedName) { result in
-					}
-				}
-				
-				// add the feed, putting it in a folder if needed
-				account.addFeed(feed) { result in
-					switch result {
-					case .success:
-						NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
-						let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
-						command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
-					case .failure:
-						command.resumeExecution(withResult:nil)
-					}
-				}
-					
+				NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
+				let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
+				command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
 			case .failure:
 				command.resumeExecution(withResult:nil)
 			}
diff --git a/Mac/Scriptability/Folder+Scriptability.swift b/Mac/Scriptability/Folder+Scriptability.swift
index bd215f855..a5ea88618 100644
--- a/Mac/Scriptability/Folder+Scriptability.swift
+++ b/Mac/Scriptability/Folder+Scriptability.swift
@@ -53,7 +53,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
     func deleteElement(_ element:ScriptingObject) {
        if let scriptableFeed = element as? ScriptableFeed {
             BatchUpdate.shared.perform {
-				folder.account?.deleteFeed(scriptableFeed.feed) { result in }
+				folder.account?.removeFeed(scriptableFeed.feed, from: folder) { result in }
             }
         }
     }
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index 907a1d012..9ba899739 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -235,7 +235,7 @@
 		84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A37CB4201ECD610087C5AF /* RenameWindowController.swift */; };
 		84A3EE5F223B667F00557320 /* DefaultFeeds.opml in Resources */ = {isa = PBXBuildFile; fileRef = 84A3EE52223B667F00557320 /* DefaultFeeds.opml */; };
 		84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */ = {isa = PBXBuildFile; fileRef = 84A3EE52223B667F00557320 /* DefaultFeeds.opml */; };
-		84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */; };
+		84AD1EAA2031617300BC20B7 /* PasteboardFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EA92031617300BC20B7 /* PasteboardFolder.swift */; };
 		84AD1EBA2031649C00BC20B7 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
 		84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EBB2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift */; };
 		84B7178C201E66580091657D /* SidebarViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */; };
@@ -843,7 +843,7 @@
 		84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToMarsEditCommand.swift; sourceTree = "<group>"; };
 		84A37CB4201ECD610087C5AF /* RenameWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RenameWindowController.swift; sourceTree = "<group>"; };
 		84A3EE52223B667F00557320 /* DefaultFeeds.opml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = DefaultFeeds.opml; sourceTree = "<group>"; };
-		84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderPasteboardWriter.swift; sourceTree = "<group>"; };
+		84AD1EA92031617300BC20B7 /* PasteboardFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardFolder.swift; sourceTree = "<group>"; };
 		84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeedPasteboardWriter.swift; sourceTree = "<group>"; };
 		84AD1EBB2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarOutlineDataSource.swift; sourceTree = "<group>"; };
 		84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SidebarViewController+ContextualMenus.swift"; sourceTree = "<group>"; };
@@ -1373,7 +1373,7 @@
 				849A97601ED9EB96007D329B /* SidebarOutlineView.swift */,
 				849A97631ED9EB96007D329B /* UnreadCountView.swift */,
 				848D578D21543519005FFAD5 /* PasteboardFeed.swift */,
-				84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */,
+				84AD1EA92031617300BC20B7 /* PasteboardFolder.swift */,
 				849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */,
 				844B5B6A1FEA224000C7C76A /* Keyboard */,
 				845A29251FC928C7007B49E3 /* Cell */,
@@ -2476,7 +2476,7 @@
 				8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */,
 				55E15BCC229D65A900D6602A /* AccountsGoogleReaderCompatibleWindowController.swift in Sources */,
 				5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */,
-				84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */,
+				84AD1EAA2031617300BC20B7 /* PasteboardFolder.swift in Sources */,
 				5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */,
 				84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */,
 				845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */,
diff --git a/Shared/Commands/DeleteCommand.swift b/Shared/Commands/DeleteCommand.swift
index afd07f1f2..6a8a15a5d 100644
--- a/Shared/Commands/DeleteCommand.swift
+++ b/Shared/Commands/DeleteCommand.swift
@@ -136,13 +136,13 @@ private struct SidebarItemSpecifier {
 
 		if let feed = feed {
 			BatchUpdate.shared.start()
-			account?.deleteFeed(feed) { result in
+			account?.removeFeed(feed, from: path.resolveContainer()) { result in
 				BatchUpdate.shared.end()
 				self.checkResult(result)
 			}
 		} else if let folder = folder {
 			BatchUpdate.shared.start()
-			account?.deleteFolder(folder) { result in
+			account?.removeFolder(folder) { result in
 				BatchUpdate.shared.end()
 				self.checkResult(result)
 			}
@@ -161,12 +161,12 @@ private struct SidebarItemSpecifier {
 
 	private func restoreFeed() {
 
-		guard let account = account, let feed = feed else {
+		guard let account = account, let feed = feed, let container = path.resolveContainer() else {
 			return
 		}
 		
 		BatchUpdate.shared.start()
-		account.restoreFeed(feed, folder: resolvedFolder()) { result in
+		account.restoreFeed(feed, container: container) { result in
 			BatchUpdate.shared.end()
 			self.checkResult(result)
 		}
@@ -187,10 +187,6 @@ private struct SidebarItemSpecifier {
 		
 	}
 
-	private func resolvedFolder() -> Folder? {
-		return path.resolveContainer() as? Folder
-	}
-	
 	private func checkResult(_ result: Result<Void, Error>) {
 		
 		switch result {
diff --git a/iOS/Add/AddFeedViewController.swift b/iOS/Add/AddFeedViewController.swift
index b2b84cee0..b2dc3464b 100644
--- a/iOS/Add/AddFeedViewController.swift
+++ b/iOS/Add/AddFeedViewController.swift
@@ -80,13 +80,10 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
 		let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)]
 		
 		var account: Account?
-		var folder: Folder?
 		if let containerAccount = container as? Account {
 			account = containerAccount
-		}
-		if let containerFolder = container as? Folder, let containerAccount = containerFolder.account {
+		} else if let containerFolder = container as? Folder, let containerAccount = containerFolder.account {
 			account = containerAccount
-			folder = containerFolder
 		}
 		
 		if account!.hasFeed(withURL: url.absoluteString) {
@@ -94,26 +91,28 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
  			return
 		}
 		
-		let title = nameTextField.text
-		
 		delegate?.processingDidBegin()
+		BatchUpdate.shared.start()
+		
+		account!.createFeed(url: url.absoluteString, name: nameTextField.text, container: container) { result in
 
-		account!.createFeed(url: url.absoluteString) { [weak self] result in
+			BatchUpdate.shared.end()
 			
 			switch result {
 			case .success(let feed):
-				self?.processFeed(feed, account: account!, folder: folder, url: url, title: title)
+				self.delegate?.processingDidEnd()
+				NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
 			case .failure(let error):
 				switch error {
 				case AccountError.createErrorAlreadySubscribed:
-					self?.showAlreadySubscribedError()
-					self?.delegate?.processingDidCancel()
+					self.showAlreadySubscribedError()
+					self.delegate?.processingDidCancel()
 				case AccountError.createErrorNotFound:
-					self?.showNoFeedsErrorMessage()
-					self?.delegate?.processingDidCancel()
+					self.showNoFeedsErrorMessage()
+					self.delegate?.processingDidCancel()
 				default:
-					self?.presentError(error)
-					self?.delegate?.processingDidCancel()
+					self.presentError(error)
+					self.delegate?.processingDidCancel()
 				}
 			}
 
@@ -178,45 +177,6 @@ private extension AddFeedViewController {
 		presentError(title: title, message: message as String)
 	}
 	
-	func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) {
-		
-		if let title = title {
-			account.renameFeed(feed, to: title) { [weak self] result in
-				switch result {
-				case .success:
-					break
-				case .failure(let error):
-					self?.presentError(error)
-				}
-			}
-		}
-		
-		if let folder = folder {
-			folder.addFeed(feed) { [weak self] result in
-				switch result {
-				case .success:
-					self?.delegate?.processingDidEnd()
-					NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
-				case .failure(let error):
-					self?.delegate?.processingDidEnd()
-					self?.presentError(error)
-				}
-			}
-		} else {
-			account.addFeed(feed) { [weak self] result in
-				switch result {
-				case .success:
-					self?.delegate?.processingDidEnd()
-					NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
-				case .failure(let error):
-					self?.delegate?.processingDidEnd()
-					self?.presentError(error)
-				}
-			}
-		}
-		
-	}
-	
 }
 
 extension AddFeedViewController: UITextFieldDelegate {
diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift
index 73a654501..4f01ed04d 100644
--- a/iOS/MasterFeed/MasterFeedViewController.swift
+++ b/iOS/MasterFeed/MasterFeedViewController.swift
@@ -373,22 +373,17 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
 		}()
 		
 		// Move the Feed
-		let source = sourceNode.parent?.representedObject as? Container
-		let destination = destParentNode?.representedObject as? Container
-		source?.removeFeed(feed) { [weak self] result in
+		guard let source = sourceNode.parent?.representedObject as? Container, let destination = destParentNode?.representedObject as? Container else {
+			return
+		}
+		
+		BatchUpdate.shared.start()
+		source.account?.moveFeed(feed, from: source, to: destination) { result in
 			switch result {
 			case .success:
-				destination?.addFeed(feed) { result in
-					switch result {
-					case .success:
-						break
-					case .failure(let error):
-						source?.addFeed(feed) { result in }
-						self?.presentError(error)
-					}
-				}
+				BatchUpdate.shared.end()
 			case .failure(let error):
-				self?.presentError(error)
+				self.presentError(error)
 			}
 		}
 
diff --git a/submodules/RSParser b/submodules/RSParser
index 718f27db5..93b481897 160000
--- a/submodules/RSParser
+++ b/submodules/RSParser
@@ -1 +1 @@
-Subproject commit 718f27db5016298a9cc650764d5d92ce54ce1e1a
+Subproject commit 93b481897d84849345daa965bd8e11860c9422e7