Merge branch 'mac-release' into main

This commit is contained in:
Maurice Parker
2021-03-27 17:21:49 -05:00
51 changed files with 449 additions and 449 deletions

View File

@@ -71,7 +71,7 @@ class AddFeedController: AddFeedWindowControllerDelegate {
return
}
account.createWebFeed(url: url.absoluteString, name: title, container: container) { result in
account.createWebFeed(url: url.absoluteString, name: title, container: container, validateFeed: true) { result in
DispatchQueue.main.async {
self.endShowingProgress()

View File

@@ -101,7 +101,7 @@ extension DetailViewController: DetailWebViewControllerDelegate {
statusBarView.mouseoverLink = link
}
func mouseDidExit(_ detailWebViewController: DetailWebViewController, link: String) {
func mouseDidExit(_ detailWebViewController: DetailWebViewController) {
guard detailWebViewController === currentWebViewController else {
return
}

View File

@@ -53,6 +53,24 @@ final class DetailWebView: WKWebView {
override func viewDidEndLiveResize() {
super.viewDidEndLiveResize()
evaluateJavaScript("document.body.style.overflow = 'visible';", completionHandler: nil)
/*
On macOS 11, when a user exits full screen
or exits zoomed mode by disconnecting an external display
the webview's `origin.y` is offset by a sizeable amount.
This code adjusts the height of the window by -1pt/+1pt,
which puts the webview back in the correct place.
*/
if #available(macOS 11, *) {
guard var frame = window?.frame else {
return
}
frame.size = NSSize(width: window!.frame.width, height: window!.frame.height - 1)
window!.setFrame(frame, display: false)
frame.size = NSSize(width: window!.frame.width, height: window!.frame.height + 1)
window!.setFrame(frame, display: false)
}
}
}

View File

@@ -14,7 +14,7 @@ import Articles
protocol DetailWebViewControllerDelegate: AnyObject {
func mouseDidEnter(_: DetailWebViewController, link: String)
func mouseDidExit(_: DetailWebViewController, link: String)
func mouseDidExit(_: DetailWebViewController)
}
final class DetailWebViewController: NSViewController, WKUIDelegate {
@@ -63,16 +63,6 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
}
override func loadView() {
// Wrap the webview in a box configured with the same background color that the web view uses
let box = NSBox(frame: .zero)
box.boxType = .custom
box.isTransparent = true
box.titlePosition = .noTitle
box.contentViewMargins = .zero
box.fillColor = NSColor(named: "webviewBackgroundColor")!
view = box
let preferences = WKPreferences()
preferences.minimumFontSize = 12.0
preferences.javaScriptCanOpenWindowsAutomatically = false
@@ -96,17 +86,11 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
webView.customUserAgent = userAgent
}
box.addSubview(webView)
view = webView
// Use the safe area layout guides if they are available.
if #available(OSX 11.0, *) {
let constraints = [
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
]
NSLayoutConstraint.activate(constraints)
// These constraints have been removed as they were unsatisfiable after removing NSBox.
} else {
let constraints = [
webView.topAnchor.constraint(equalTo: view.topAnchor),
@@ -194,8 +178,8 @@ extension DetailWebViewController: WKScriptMessageHandler {
if message.name == MessageName.mouseDidEnter, let link = message.body as? String {
delegate?.mouseDidEnter(self, link: link)
}
else if message.name == MessageName.mouseDidExit, let link = message.body as? String{
delegate?.mouseDidExit(self, link: link)
else if message.name == MessageName.mouseDidExit {
delegate?.mouseDidExit(self)
}
}
}
@@ -251,6 +235,8 @@ private extension DetailWebViewController {
}
func reloadHTML() {
delegate?.mouseDidExit(self)
let style = ArticleStylesManager.shared.currentStyle
let rendering: ArticleRenderer.Rendering

View File

@@ -17,15 +17,15 @@ final class IconView: NSView {
if NSApplication.shared.effectiveAppearance.isDarkMode {
if self.iconImage?.isDark ?? false {
self.isDisconcernable = false
self.isDiscernable = false
} else {
self.isDisconcernable = true
self.isDiscernable = true
}
} else {
if self.iconImage?.isBright ?? false {
self.isDisconcernable = false
self.isDiscernable = false
} else {
self.isDisconcernable = true
self.isDiscernable = true
}
}
@@ -35,7 +35,7 @@ final class IconView: NSView {
}
}
private var isDisconcernable = true
private var isDiscernable = true
override var isFlipped: Bool {
return true
@@ -85,7 +85,7 @@ final class IconView: NSView {
override func draw(_ dirtyRect: NSRect) {
guard !(iconImage?.isBackgroundSupressed ?? false) else { return }
guard hasExposedVerticalBackground || !isDisconcernable else { return }
guard hasExposedVerticalBackground || !isDiscernable else { return }
let color = NSApplication.shared.effectiveAppearance.isDarkMode ? IconView.darkBackgroundColor : IconView.lightBackgroundColor
color.set()

View File

@@ -203,13 +203,13 @@ private extension SidebarOutlineDataSource {
return SidebarOutlineDataSource.dragOperationNone
}
if parentNode == dropTargetNode && index == NSOutlineViewDropOnItemIndex {
return localDragOperation()
return localDragOperation(parentNode: parentNode)
}
let updatedIndex = indexWhereDraggedFeedWouldAppear(dropTargetNode, draggedFeed)
if parentNode !== dropTargetNode || index != updatedIndex {
outlineView.setDropItem(dropTargetNode, dropChildIndex: updatedIndex)
}
return localDragOperation()
return localDragOperation(parentNode: parentNode)
}
func validateLocalFeedsDrop(_ outlineView: NSOutlineView, _ draggedFeeds: Set<PasteboardWebFeed>, _ parentNode: Node, _ index: Int) -> NSDragOperation {
@@ -226,14 +226,19 @@ private extension SidebarOutlineDataSource {
if parentNode !== dropTargetNode || index != NSOutlineViewDropOnItemIndex {
outlineView.setDropItem(dropTargetNode, dropChildIndex: NSOutlineViewDropOnItemIndex)
}
return localDragOperation()
return localDragOperation(parentNode: parentNode)
}
func localDragOperation() -> NSDragOperation {
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
return .copy
func localDragOperation(parentNode: Node) -> NSDragOperation {
guard let firstDraggedNode = draggedNodes?.first else { return .move }
if sameAccount(firstDraggedNode, parentNode) {
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
return .copy
} else {
return .move
}
} else {
return .move
return .copy
}
}
@@ -282,7 +287,7 @@ private extension SidebarOutlineDataSource {
if index != updatedIndex {
outlineView.setDropItem(parentNode, dropChildIndex: updatedIndex)
}
return localDragOperation()
return localDragOperation(parentNode: parentNode)
}
func validateLocalFoldersDrop(_ outlineView: NSOutlineView, _ draggedFolders: Set<PasteboardFolder>, _ parentNode: Node, _ index: Int) -> NSDragOperation {
@@ -300,7 +305,7 @@ private extension SidebarOutlineDataSource {
if index != NSOutlineViewDropOnItemIndex {
outlineView.setDropItem(parentNode, dropChildIndex: NSOutlineViewDropOnItemIndex)
}
return localDragOperation()
return localDragOperation(parentNode: parentNode)
}
func copyWebFeedInAccount(node: Node, to parentNode: Node) {
@@ -354,7 +359,7 @@ private extension SidebarOutlineDataSource {
}
}
} else {
destinationAccount.createWebFeed(url: feed.url, name: feed.editedName, container: destinationContainer) { result in
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationContainer, validateFeed: false) { result in
switch result {
case .success:
break
@@ -365,60 +370,6 @@ private extension SidebarOutlineDataSource {
}
}
func moveWebFeedBetweenAccounts(node: Node, to parentNode: Node) {
guard let feed = node.representedObject as? WebFeed,
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.existingWebFeed(withURL: feed.url) {
BatchUpdate.shared.start()
destinationAccount.addWebFeed(existingFeed, to: destinationContainer) { result in
switch result {
case .success:
sourceAccount.removeWebFeed(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):
BatchUpdate.shared.end()
NSApplication.shared.presentError(error)
}
}
} else {
BatchUpdate.shared.start()
destinationAccount.createWebFeed(url: feed.url, name: feed.editedName, container: destinationContainer) { result in
switch result {
case .success:
sourceAccount.removeWebFeed(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):
BatchUpdate.shared.end()
NSApplication.shared.presentError(error)
}
}
}
}
func acceptLocalFeedsDrop(_ outlineView: NSOutlineView, _ draggedFeeds: Set<PasteboardWebFeed>, _ parentNode: Node, _ index: Int) -> Bool {
guard let draggedNodes = draggedNodes else {
return false
@@ -432,11 +383,7 @@ private extension SidebarOutlineDataSource {
moveWebFeedInAccount(node: node, to: parentNode)
}
} else {
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
copyWebFeedBetweenAccounts(node: node, to: parentNode)
} else {
moveWebFeedBetweenAccounts(node: node, to: parentNode)
}
copyWebFeedBetweenAccounts(node: node, to: parentNode)
}
}
@@ -476,42 +423,17 @@ private extension SidebarOutlineDataSource {
}
func copyFolderBetweenAccounts(node: Node, to parentNode: Node) {
guard let sourceFolder = node.representedObject as? Folder,
guard let folder = 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
}
replicateFolder(sourceFolder, destinationAccount: destinationAccount) {
sourceAccount.removeFolder(sourceFolder) { result in
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.topLevelWebFeeds {
if let existingFeed = destinationAccount.existingWebFeed(withURL: feed.url) {
group.enter()
destinationAccount.addWebFeed(existingFeed, to: destinationFolder) { result in
group.leave()
switch result {
case .success:
break
@@ -520,9 +442,7 @@ private extension SidebarOutlineDataSource {
}
}
} else {
group.enter()
destinationAccount.createWebFeed(url: feed.url, name: feed.editedName, container: destinationFolder) { result in
group.leave()
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationFolder, validateFeed: false) { result in
switch result {
case .success:
break
@@ -532,12 +452,8 @@ private extension SidebarOutlineDataSource {
}
}
}
group.notify(queue: DispatchQueue.main) {
completion()
}
case .failure(let error):
NSApplication.shared.presentError(error)
completion()
}
}
@@ -550,11 +466,7 @@ private extension SidebarOutlineDataSource {
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)
}
copyFolderBetweenAccounts(node: node, to: parentNode)
}
}

View File

@@ -255,6 +255,11 @@ protocol SidebarDelegate: AnyObject {
guard outlineView.clickedRow == outlineView.selectedRow else {
return
}
if AppDefaults.shared.feedDoubleClickMarkAsRead, let articles = try? singleSelectedWebFeed?.fetchUnreadArticles() {
if let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) {
runCommand(markReadCommand)
}
}
openInBrowser(sender)
}