mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge branch 'mac-release' into main
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user