diff --git a/Shared/Article Rendering/ArticleRenderer.swift b/Shared/Article Rendering/ArticleRenderer.swift
index 4dfe61757..7643b1091 100644
--- a/Shared/Article Rendering/ArticleRenderer.swift
+++ b/Shared/Article Rendering/ArticleRenderer.swift
@@ -365,6 +365,9 @@ private extension ArticleRenderer {
func renderHTML(withBody body: String) -> String {
var s = "
\n"
+ if let baseURL = baseURL {
+ s += ("")
+ }
s += "\n"
s += title.htmlBySurroundingWithTag("title")
s += styleString().htmlBySurroundingWithTag("style")
diff --git a/iOS/Detail/DetailViewController.swift b/iOS/Detail/DetailViewController.swift
index 338f67998..0b9f81269 100644
--- a/iOS/Detail/DetailViewController.swift
+++ b/iOS/Detail/DetailViewController.swift
@@ -64,7 +64,7 @@ class DetailViewController: UIViewController {
}
let style = ArticleStylesManager.shared.currentStyle
let html = ArticleRenderer.articleHTML(article: article, style: style)
- webView.loadHTMLString(html, baseURL: article.baseURL)
+ webView.loadHTMLString(html, baseURL: nil)
}
@objc func statusesDidChange(_ note: Notification) {
@@ -155,33 +155,5 @@ extension DetailViewController: WKNavigationDelegate {
}
}
-}
-
-private extension Article {
-
- var baseURL: URL? {
- var s = url
- if s == nil {
- s = feed?.homePageURL
- }
- if s == nil {
- s = feed?.url
- }
-
- guard let urlString = s else {
- return nil
- }
- var urlComponents = URLComponents(string: urlString)
- if urlComponents == nil {
- return nil
- }
-
- // Can’t use url-with-fragment as base URL. The webview won’t load. See scripting.com/rss.xml for example.
- urlComponents!.fragment = nil
- guard let url = urlComponents!.url, url.scheme == "http" || url.scheme == "https" else {
- return nil
- }
- return url
- }
}
diff --git a/iOS/Extensions/String-Extensions.swift b/iOS/Extensions/String-Extensions.swift
index d546ce63f..c56dda628 100644
--- a/iOS/Extensions/String-Extensions.swift
+++ b/iOS/Extensions/String-Extensions.swift
@@ -1,5 +1,5 @@
//
-// String+.swift
+// String-Extensions.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/8/19.
diff --git a/iOS/Extensions/UIImage-Extensions.swift b/iOS/Extensions/UIImage-Extensions.swift
index 79aff8d1f..e9e071c40 100644
--- a/iOS/Extensions/UIImage-Extensions.swift
+++ b/iOS/Extensions/UIImage-Extensions.swift
@@ -1,4 +1,10 @@
-//Copyright © 2019 Vincode, Inc. All rights reserved.
+//
+// UIImage-Extensions.swift
+// NetNewsWire
+//
+// Created by Maurice Parker on 4/18/19.
+// Copyright © 2019 Ranchero Software. All rights reserved.
+//
import UIKit
diff --git a/iOS/Extensions/UISplitViewController-Extensions.swift b/iOS/Extensions/UISplitViewController-Extensions.swift
index 617b417eb..98fa33c5d 100644
--- a/iOS/Extensions/UISplitViewController-Extensions.swift
+++ b/iOS/Extensions/UISplitViewController-Extensions.swift
@@ -1,4 +1,10 @@
-//Copyright © 2019 Ranchero Software. All rights reserved.
+//
+// UISplitViewController-Extensions.swift
+// NetNewsWire
+//
+// Created by Maurice Parker on 4/18/19.
+// Copyright © 2019 Ranchero Software. All rights reserved.
+//
import UIKit
diff --git a/iOS/Extensions/UIStoryboard-Extensions.swift b/iOS/Extensions/UIStoryboard-Extensions.swift
index 66bfc485a..540b96e22 100644
--- a/iOS/Extensions/UIStoryboard-Extensions.swift
+++ b/iOS/Extensions/UIStoryboard-Extensions.swift
@@ -1,5 +1,5 @@
//
-// UIStoryboard+.swift
+// UIStoryboard-Extensions.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/8/19.
diff --git a/iOS/Master/Cell/MasterTableViewCell.swift b/iOS/Master/Cell/MasterTableViewCell.swift
index 4ab775021..8cea92c37 100644
--- a/iOS/Master/Cell/MasterTableViewCell.swift
+++ b/iOS/Master/Cell/MasterTableViewCell.swift
@@ -32,14 +32,6 @@ class MasterTableViewCell : UITableViewCell {
}
}
- var indent = false {
- didSet {
- if indent != oldValue {
- setNeedsLayout()
- }
- }
- }
-
var disclosureExpanded = false {
didSet {
updateDisclosureImage()
@@ -115,12 +107,12 @@ class MasterTableViewCell : UITableViewCell {
override func willTransition(to state: UITableViewCell.StateMask) {
super.willTransition(to: state)
- showingEditControl = state == .showingEditControl
+ showingEditControl = state.contains(.showingEditControl)
}
override func layoutSubviews() {
super.layoutSubviews()
- let layout = MasterTableViewCellLayout(cellSize: bounds.size, shouldShowImage: shouldShowImage, label: titleView, unreadCountView: unreadCountView, showingEditingControl: showingEditControl, indent: indent, shouldShowDisclosure: true)
+ let layout = MasterTableViewCellLayout(cellSize: bounds.size, insets: safeAreaInsets, shouldShowImage: shouldShowImage, label: titleView, unreadCountView: unreadCountView, showingEditingControl: showingEditControl, indent: indentationLevel == 1, shouldShowDisclosure: !showsReorderControl)
layoutWith(layout)
}
@@ -167,18 +159,10 @@ private extension MasterTableViewCell {
}
func layoutWith(_ layout: MasterTableViewCellLayout) {
- faviconImageView.rs_setFrameIfNotEqual(layout.faviconRect)
- titleView.rs_setFrameIfNotEqual(layout.titleRect)
- unreadCountView.rs_setFrameIfNotEqual(layout.unreadCountRect)
- disclosureButton?.rs_setFrameIfNotEqual(layout.disclosureButtonRect)
+ faviconImageView.setFrameIfNotEqual(layout.faviconRect)
+ titleView.setFrameIfNotEqual(layout.titleRect)
+ unreadCountView.setFrameIfNotEqual(layout.unreadCountRect)
+ disclosureButton?.setFrameIfNotEqual(layout.disclosureButtonRect)
}
}
-
-extension UIView {
- func rs_setFrameIfNotEqual(_ rect: CGRect) {
- if !self.frame.equalTo(rect) {
- self.frame = rect
- }
- }
-}
diff --git a/iOS/Master/Cell/MasterTableViewCellLayout.swift b/iOS/Master/Cell/MasterTableViewCellLayout.swift
index f22621742..cc692740c 100644
--- a/iOS/Master/Cell/MasterTableViewCellLayout.swift
+++ b/iOS/Master/Cell/MasterTableViewCellLayout.swift
@@ -11,6 +11,8 @@ import RSCore
struct MasterTableViewCellLayout {
+ private static let indent = CGFloat(integerLiteral: 14)
+ private static let editingControlIndent = CGFloat(integerLiteral: 40)
private static let imageSize = CGSize(width: 16, height: 16)
private static let marginLeft = CGFloat(integerLiteral: 8)
private static let imageMarginRight = CGFloat(integerLiteral: 8)
@@ -23,16 +25,22 @@ struct MasterTableViewCellLayout {
let unreadCountRect: CGRect
let disclosureButtonRect: CGRect
- init(cellSize: CGSize, shouldShowImage: Bool, label: UILabel, unreadCountView: MasterUnreadCountView, showingEditingControl: Bool, indent: Bool, shouldShowDisclosure: Bool) {
-
- let bounds = CGRect(x: 0.0, y: 0.0, width: floor(cellSize.width), height: floor(cellSize.height))
+ init(cellSize: CGSize, insets: UIEdgeInsets, shouldShowImage: Bool, label: UILabel, unreadCountView: MasterUnreadCountView, showingEditingControl: Bool, indent: Bool, shouldShowDisclosure: Bool) {
+ var initialIndent = MasterTableViewCellLayout.marginLeft + insets.left
+ if indent {
+ initialIndent += MasterTableViewCellLayout.indent
+ }
+ if showingEditingControl {
+ initialIndent += MasterTableViewCellLayout.editingControlIndent
+ }
+
+ let bounds = CGRect(x: initialIndent, y: 0.0, width: floor(cellSize.width - initialIndent - insets.right), height: floor(cellSize.height))
+
// Favicon
var rFavicon = CGRect.zero
if shouldShowImage {
- var indentX = showingEditingControl ? MasterTableViewCellLayout.marginLeft + 40 : MasterTableViewCellLayout.marginLeft
- indentX = indent ? indentX + 20 : indentX
- rFavicon = CGRect(x: indentX, y: 0.0, width: MasterTableViewCellLayout.imageSize.width, height: MasterTableViewCellLayout.imageSize.height)
+ rFavicon = CGRect(x: bounds.origin.x, y: 0.0, width: MasterTableViewCellLayout.imageSize.width, height: MasterTableViewCellLayout.imageSize.height)
rFavicon = MasterTableViewCellLayout.centerVertically(rFavicon, bounds)
}
self.faviconRect = rFavicon
@@ -44,7 +52,7 @@ struct MasterTableViewCellLayout {
if shouldShowImage {
rLabel.origin.x = rFavicon.maxX + MasterTableViewCellLayout.imageMarginRight
} else {
- rLabel.origin.x = indent ? MasterTableViewCellLayout.marginLeft + 10 : MasterTableViewCellLayout.marginLeft
+ rLabel.origin.x = bounds.minX
}
rLabel = MasterTableViewCellLayout.centerVertically(rLabel, bounds)
diff --git a/iOS/Master/Cell/MasterTableViewSectionHeader.swift b/iOS/Master/Cell/MasterTableViewSectionHeader.swift
index df91732be..ed3d6ae94 100644
--- a/iOS/Master/Cell/MasterTableViewSectionHeader.swift
+++ b/iOS/Master/Cell/MasterTableViewSectionHeader.swift
@@ -71,7 +71,7 @@ class MasterTableViewSectionHeader: UITableViewHeaderFooterView {
override func layoutSubviews() {
super.layoutSubviews()
- let layout = MasterTableViewCellLayout(cellSize: bounds.size, shouldShowImage: false, label: titleView, unreadCountView: unreadCountView, showingEditingControl: false, indent: true, shouldShowDisclosure: false)
+ let layout = MasterTableViewCellLayout(cellSize: bounds.size, insets: safeAreaInsets, shouldShowImage: false, label: titleView, unreadCountView: unreadCountView, showingEditingControl: false, indent: true, shouldShowDisclosure: false)
layoutWith(layout)
}
@@ -80,7 +80,9 @@ class MasterTableViewSectionHeader: UITableViewHeaderFooterView {
private extension MasterTableViewSectionHeader {
func commonInit() {
- backgroundColor = AppAssets.tableSectionHeaderColor
+ let view = UIView()
+ view.backgroundColor = AppAssets.tableSectionHeaderColor
+ backgroundView = view
addSubviewAtInit(unreadCountView)
addSubviewAtInit(titleView)
}
@@ -91,8 +93,8 @@ private extension MasterTableViewSectionHeader {
}
func layoutWith(_ layout: MasterTableViewCellLayout) {
- titleView.rs_setFrameIfNotEqual(layout.titleRect)
- unreadCountView.rs_setFrameIfNotEqual(layout.unreadCountRect)
+ titleView.setFrameIfNotEqual(layout.titleRect)
+ unreadCountView.setFrameIfNotEqual(layout.unreadCountRect)
}
}
diff --git a/iOS/Master/MasterViewController.swift b/iOS/Master/MasterViewController.swift
index cb855e307..a77dc206f 100644
--- a/iOS/Master/MasterViewController.swift
+++ b/iOS/Master/MasterViewController.swift
@@ -268,6 +268,110 @@ class MasterViewController: UITableViewController, UndoableCommandRunner {
self.navigationController?.pushViewController(timeline, animated: true)
}
+
+ override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
+ guard let node = nodeFor(indexPath) else {
+ return false
+ }
+ return node.representedObject is Feed
+ }
+
+ override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
+
+ // Adjust the index path so that it will never be in the smart feeds area
+ let destIndexPath: IndexPath = {
+ if proposedDestinationIndexPath.section == 0 {
+ return IndexPath(row: 0, section: 1)
+ }
+ return proposedDestinationIndexPath
+ }()
+
+ guard let draggedNode = nodeFor(sourceIndexPath), let destNode = nodeFor(destIndexPath), let parentNode = destNode.parent else {
+ assertionFailure("This should never happen")
+ return sourceIndexPath
+ }
+
+ // If this is a folder and isn't expanded or doesn't have any entries, let the users drop on it
+ if destNode.representedObject is Folder && (destNode.numberOfChildNodes == 0 || !expandedNodes.contains(destNode)) {
+ let movementAdjustment = sourceIndexPath > destIndexPath ? 1 : 0
+ return IndexPath(row: destIndexPath.row + movementAdjustment, section: destIndexPath.section)
+ }
+
+ // If we are dragging around in the same container, just return the original source
+ if parentNode.childNodes.contains(draggedNode) {
+ return sourceIndexPath
+ }
+
+ // Suggest to the user the best place to drop the feed
+ // Revisit if the tree controller can ever be sorted in some other way.
+ let nodes = parentNode.childNodes + [draggedNode]
+ var sortedNodes = nodes.sortedAlphabeticallyWithFoldersAtEnd()
+ let index = sortedNodes.firstIndex(of: draggedNode)!
+
+ if index == 0 {
+
+ if parentNode.representedObject is Account {
+ return IndexPath(row: 0, section: destIndexPath.section)
+ } else {
+ return indexPathFor(parentNode)!
+ }
+
+ } else {
+
+ sortedNodes.remove(at: index)
+
+ let movementAdjustment = sourceIndexPath < destIndexPath ? 1 : 0
+ let adjustedIndex = index - movementAdjustment
+ if adjustedIndex >= sortedNodes.count {
+ let lastSortedIndexPath = indexPathFor(sortedNodes[sortedNodes.count - 1])!
+ return IndexPath(row: lastSortedIndexPath.row + 1, section: lastSortedIndexPath.section)
+ } else {
+ return indexPathFor(sortedNodes[adjustedIndex])!
+ }
+
+ }
+
+ }
+
+ override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
+
+ guard let sourceNode = nodeFor(sourceIndexPath), let feed = sourceNode.representedObject as? Feed else {
+ return
+ }
+
+ // Based on the drop we have to determine a node to start looking for a parent container.
+ let destNode: Node = {
+ if destinationIndexPath.row == 0 {
+ return treeController.rootNode.childAtIndex(destinationIndexPath.section)!
+ } else {
+ let movementAdjustment = sourceIndexPath > destinationIndexPath ? 1 : 0
+ let adjustedDestIndexPath = IndexPath(row: destinationIndexPath.row - movementAdjustment, section: destinationIndexPath.section)
+ return nodeFor(adjustedDestIndexPath)!
+ }
+ }()
+
+ // Now we start looking for the parent container
+ let destParentNode: Node? = {
+ if destNode.representedObject is Container {
+ return destNode
+ } else {
+ if destNode.parent?.representedObject is Container {
+ return destNode.parent!
+ } else {
+ return nil
+ }
+ }
+ }()
+
+ // Move the Feed
+ let account = accountForNode(destNode)
+ let sourceContainer = sourceNode.parent?.representedObject as? Container
+ let destinationFolder = destParentNode?.representedObject as? Folder
+ sourceContainer?.deleteFeed(feed)
+ account?.addFeed(feed, to: destinationFolder)
+ account?.structureDidChange()
+
+ }
// MARK: Actions
@@ -383,7 +487,11 @@ class MasterViewController: UITableViewController, UndoableCommandRunner {
func configure(_ cell: MasterTableViewCell, _ node: Node) {
cell.delegate = self
- cell.indent = node.parent?.representedObject is Folder
+ if node.parent?.representedObject is Folder {
+ cell.indentationLevel = 1
+ } else {
+ cell.indentationLevel = 0
+ }
cell.disclosureExpanded = expandedNodes.contains(node)
cell.allowDisclosureSelection = node.canHaveChildNodes
@@ -570,7 +678,20 @@ private extension MasterViewController {
callback(cell as! MasterTableViewCell, node)
}
}
-
+
+ private func accountForNode(_ node: Node) -> Account? {
+ if let account = node.representedObject as? Account {
+ return account
+ }
+ if let folder = node.representedObject as? Folder {
+ return folder.account
+ }
+ if let feed = node.representedObject as? Feed {
+ return feed.account
+ }
+ return nil
+ }
+
func rebuildShadowTable() {
for i in 0..