From 06278879bc654a25f9fd3b8b8af5d5742d9a5674 Mon Sep 17 00:00:00 2001 From: Ethan Wong Date: Mon, 10 Jan 2022 08:16:42 +0800 Subject: [PATCH 01/70] Fix: bottom inset of TimelineTableView disappeared on macOS Monterey --- Mac/MainWindow/Timeline/TimelineViewController.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 1467649e4..3e04feeb3 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -812,6 +812,12 @@ extension TimelineViewController: NSTableViewDataSource { } return ArticlePasteboardWriter(article: article) } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + // Keeping -[NSTableViewDelegate tableView:heightOfRow:] implemented fixes + // an issue that the bottom inset of NSTableView disappears on macOS Monterey. + return tableView.rowHeight + } } // MARK: - NSTableViewDelegate From 08a2b5690e45230e8320be5e8f6fa9da4b8a86cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Fri, 21 Jan 2022 23:53:20 +0100 Subject: [PATCH 02/70] Remove leftover comment unrelated to any key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it may seem very strange to some people that I'm creating a pull request to fix such a non-issue, here's the explanation: I'm working on a tool that reads Strings files (similar to https://github.com/Flinesoft/BartyCrouch) and my Strings file parser fails in my "real-world project tests" due to this comment. 😅 --- .../Resources/tr.lproj/Sparkle.strings | Bin 9246 -> 9138 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Mac/Resources/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/tr.lproj/Sparkle.strings b/Mac/Resources/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app/Contents/Resources/tr.lproj/Sparkle.strings index 4def140e5a928b657fd13fd7aa009ba026f42493..d96bec90e0725dffe45f5721aff135ee44125fe2 100644 GIT binary patch delta 16 XcmbQ|vB`ad5!>WCj*QJL>=Lp7HPraWMb@U)=_O From b11db14017e5a6a7578229f46346b0f9b9882025 Mon Sep 17 00:00:00 2001 From: J-rg Date: Fri, 21 Jan 2022 23:19:28 +0100 Subject: [PATCH 03/70] Fix blurry sidebar symbols (#3268) --- Mac/AppAssets.swift | 12 ++++-------- Mac/MainWindow/IconView.swift | 3 +++ Mac/MainWindow/Sidebar/Cell/SidebarCell.swift | 11 +++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Mac/AppAssets.swift b/Mac/AppAssets.swift index 74c69df40..85b560fe7 100644 --- a/Mac/AppAssets.swift +++ b/Mac/AppAssets.swift @@ -159,8 +159,7 @@ struct AppAssets { if #available(macOS 11.0, *) { let image = NSImage(systemSymbolName: "folder", accessibilityDescription: nil)! let preferredColor = NSColor(named: "AccentColor")! - let coloredImage = image.tinted(with: preferredColor) - return IconImage(coloredImage, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) + return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) } else { return IconImage(RSImage(named: NSImage.folderName)!) } @@ -255,8 +254,7 @@ struct AppAssets { if #available(macOS 11.0, *) { let image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: nil)! let preferredColor = NSColor(named: "StarColor")! - let coloredImage = image.tinted(with: preferredColor) - return IconImage(coloredImage, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) + return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) } else { return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!, isBackgroundSupressed: true) } @@ -278,8 +276,7 @@ struct AppAssets { if #available(macOS 11.0, *) { let image = NSImage(systemSymbolName: "sun.max.fill", accessibilityDescription: nil)! let preferredColor = NSColor.orange - let coloredImage = image.tinted(with: preferredColor) - return IconImage(coloredImage, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) + return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) } else { return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!, isBackgroundSupressed: true) } @@ -289,8 +286,7 @@ struct AppAssets { if #available(macOS 11.0, *) { let image = NSImage(systemSymbolName: "largecircle.fill.circle", accessibilityDescription: nil)! let preferredColor = NSColor(named: "AccentColor")! - let coloredImage = image.tinted(with: preferredColor) - return IconImage(coloredImage, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) + return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: preferredColor.cgColor) } else { return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!, isBackgroundSupressed: true) } diff --git a/Mac/MainWindow/IconView.swift b/Mac/MainWindow/IconView.swift index 43e4c8444..cc165d9de 100644 --- a/Mac/MainWindow/IconView.swift +++ b/Mac/MainWindow/IconView.swift @@ -14,6 +14,9 @@ final class IconView: NSView { didSet { if iconImage !== oldValue { imageView.image = iconImage?.image + if let tintColor = iconImage?.preferredColor { + imageView.contentTintColor = NSColor(cgColor: tintColor) + } if NSApplication.shared.effectiveAppearance.isDarkMode { if self.iconImage?.isDark ?? false { diff --git a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift index ae0e2a12d..52b3804b4 100644 --- a/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift +++ b/Mac/MainWindow/Sidebar/Cell/SidebarCell.swift @@ -143,18 +143,17 @@ private extension SidebarCell { var updatedIconImage = iconImage if let iconImage = iconImage, iconImage.isSymbol { + var tintColor: CGColor if backgroundStyle != .normal { - let image = iconImage.image.tinted(with: .white) - updatedIconImage = IconImage(image, isSymbol: iconImage.isSymbol, isBackgroundSupressed: iconImage.isBackgroundSupressed) + tintColor = NSColor.white.cgColor } else { if let preferredColor = iconImage.preferredColor { - let image = iconImage.image.tinted(with: NSColor(cgColor: preferredColor)!) - updatedIconImage = IconImage(image, isSymbol: iconImage.isSymbol, isBackgroundSupressed: iconImage.isBackgroundSupressed) + tintColor = preferredColor } else { - let image = iconImage.image.tinted(with: .controlAccentColor) - updatedIconImage = IconImage(image, isSymbol: iconImage.isSymbol, isBackgroundSupressed: iconImage.isBackgroundSupressed) + tintColor = NSColor.controlAccentColor.cgColor } } + updatedIconImage = IconImage(iconImage.image, isSymbol: iconImage.isSymbol, isBackgroundSupressed: iconImage.isBackgroundSupressed, preferredColor: tintColor) } if let image = updatedIconImage { From 0fb4398c1080878b5ef3ef38f64899f52585cabc Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 23 Jan 2022 13:20:36 -0800 Subject: [PATCH 04/70] Update to point to the 6.1 help book. --- iOS/Settings/SettingsViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 69f4a9829..89bf9c8c6 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -243,7 +243,7 @@ class SettingsViewController: UITableViewController { case 7: switch indexPath.row { case 0: - openURL("https://netnewswire.com/help/ios/6.0/en/") + openURL("https://netnewswire.com/help/ios/6.1/en/") tableView.selectRow(at: nil, animated: true, scrollPosition: .none) case 1: openURL("https://netnewswire.com/") From ea7a101ca53977536857754daf81bf0ccc8fcf3d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 23 Jan 2022 13:21:45 -0800 Subject: [PATCH 05/70] Change to point to the 6.1 documentation --- Mac/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index ad024b046..db115b936 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -679,7 +679,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, @IBAction func showHelp(_ sender: Any?) { - Browser.open("https://netnewswire.com/help/mac/6.0/en/", inBackground: false) + Browser.open("https://netnewswire.com/help/mac/6.1/en/", inBackground: false) } @IBAction func donateToAppCampForGirls(_ sender: Any?) { From 66ff2c8beddd88d680fe30881e8ae7839c006291 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 23 Jan 2022 16:00:44 -0800 Subject: [PATCH 06/70] Rename UnifiedWindow to MainWindow and remove the old MainWindow since we no longer support Catalina --- Mac/AppDelegate.swift | 2 +- Mac/Base.lproj/MainWindow.storyboard | 257 +--------------- Mac/Base.lproj/UnifiedWindow.storyboard | 390 ------------------------ NetNewsWire.xcodeproj/project.pbxproj | 14 - 4 files changed, 15 insertions(+), 648 deletions(-) delete mode 100644 Mac/Base.lproj/UnifiedWindow.storyboard diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 3307734d8..24bb93f2b 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -396,7 +396,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, func createMainWindowController() -> MainWindowController { let controller: MainWindowController - controller = windowControllerWithName("UnifiedWindow") as! MainWindowController + controller = windowControllerWithName("MainWindow") as! MainWindowController if !(mainWindowController?.isOpen ?? false) { mainWindowControllers.removeAll() diff --git a/Mac/Base.lproj/MainWindow.storyboard b/Mac/Base.lproj/MainWindow.storyboard index 6b01165e4..2a537adbc 100644 --- a/Mac/Base.lproj/MainWindow.storyboard +++ b/Mac/Base.lproj/MainWindow.storyboard @@ -11,241 +11,22 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -309,20 +90,20 @@ - + - - + + - + - + @@ -335,7 +116,7 @@ - + @@ -353,7 +134,7 @@ - + @@ -469,7 +250,7 @@ - + @@ -503,7 +284,7 @@ - + - + @@ -530,7 +311,7 @@ - + @@ -604,16 +385,6 @@ - - - - - - - - - - diff --git a/Mac/Base.lproj/UnifiedWindow.storyboard b/Mac/Base.lproj/UnifiedWindow.storyboard deleted file mode 100644 index 2a537adbc..000000000 --- a/Mac/Base.lproj/UnifiedWindow.storyboard +++ /dev/null @@ -1,390 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 71c55e9a3..8adb942c4 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -340,8 +340,6 @@ 51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; }; 51BC4B00247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; }; 51BC4B01247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; }; - 51C03081257D815A00609262 /* UnifiedWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51C0307F257D815A00609262 /* UnifiedWindow.storyboard */; }; - 51C03082257D815A00609262 /* UnifiedWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51C0307F257D815A00609262 /* UnifiedWindow.storyboard */; }; 51C266EA238C334800F53014 /* ContextMenuPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C266E9238C334800F53014 /* ContextMenuPreviewViewController.swift */; }; 51C45258226508CF00C03939 /* AppAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45254226507D200C03939 /* AppAssets.swift */; }; 51C45259226508D300C03939 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45255226507D200C03939 /* AppDefaults.swift */; }; @@ -1306,7 +1304,6 @@ 51BB7C302335ACDE008E8144 /* page.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = page.html; sourceTree = ""; }; 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = ""; }; 51BEB22C2451E8340066DEDD /* TwitterEnterDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterEnterDetailTableViewController.swift; sourceTree = ""; }; - 51C03080257D815A00609262 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Mac/Base.lproj/UnifiedWindow.storyboard; sourceTree = SOURCE_ROOT; }; 51C266E9238C334800F53014 /* ContextMenuPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuPreviewViewController.swift; sourceTree = ""; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; }; @@ -2239,7 +2236,6 @@ isa = PBXGroup; children = ( 8483630C2262A3FE00DA1D35 /* MainWindow.storyboard */, - 51C0307F257D815A00609262 /* UnifiedWindow.storyboard */, 849A975D1ED9EB72007D329B /* MainWindowController.swift */, 519B8D322143397200FA689C /* SharingServiceDelegate.swift */, 849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */, @@ -3370,7 +3366,6 @@ 5103A9F5242258C600410853 /* AccountsAddCloudKit.xib in Resources */, 65ED405E235DEF6C0081F399 /* DefaultFeeds.opml in Resources */, 51333D3C2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */, - 51C03082257D815A00609262 /* UnifiedWindow.storyboard in Resources */, 65ED405F235DEF6C0081F399 /* Preferences.storyboard in Resources */, 65ED4061235DEF6C0081F399 /* Assets.xcassets in Resources */, 65ED4063235DEF6C0081F399 /* RenameSheet.xib in Resources */, @@ -3472,7 +3467,6 @@ 844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */, 5103A9F4242258C600410853 /* AccountsAddCloudKit.xib in Resources */, 84A3EE5F223B667F00557320 /* DefaultFeeds.opml in Resources */, - 51C03081257D815A00609262 /* UnifiedWindow.storyboard in Resources */, 849C78902362AAFC009A71E4 /* ExportOPMLSheet.xib in Resources */, 84C9FC8222629E4800D921D6 /* Preferences.storyboard in Resources */, 849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */, @@ -4528,14 +4522,6 @@ name = AddTwitterFeedSheet.xib; sourceTree = ""; }; - 51C0307F257D815A00609262 /* UnifiedWindow.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 51C03080257D815A00609262 /* Base */, - ); - name = UnifiedWindow.storyboard; - sourceTree = ""; - }; 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */ = { isa = PBXVariantGroup; children = ( From 4cc4b59da270c2062277b0eeddad0fd24010d749 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 23 Jan 2022 16:24:47 -0800 Subject: [PATCH 07/70] Update version and release notes for first 6.1 TestFlight beta. --- Technotes/ReleaseNotes-iOS.markdown | 9 +++++++++ xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Technotes/ReleaseNotes-iOS.markdown b/Technotes/ReleaseNotes-iOS.markdown index 4150dfb6c..ec358a074 100644 --- a/Technotes/ReleaseNotes-iOS.markdown +++ b/Technotes/ReleaseNotes-iOS.markdown @@ -1,5 +1,14 @@ # iOS Release Notes +### 6.1 TestFlight build 6100 - 23 Jan 2022 + +Article themes. Several themes ship with the app, and you can create your own. You can change the theme in Preferences. +Copy URLs using repaired, rather than raw, feed links. +Disallow creation of iCloud account in the app if iCloud and iCloud Drive aren’t both enabled. +Fixed bug showing quote tweets that only included an image. +Video autoplay is now disallowed. +Article view now supports RTL layout. + ### 6.0.1 TestFlight build 608 - 28 Aug 2021 Fixed our top crashing bug — it could happen when updating a table view diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 3d6d7a3de..3be188d1a 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it -MARKETING_VERSION = 6.0.2 -CURRENT_PROJECT_VERSION = 609 +MARKETING_VERSION = 6.1 +CURRENT_PROJECT_VERSION = 6100 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From b9201ad10db21f6fafa9c88425332018f19f59a1 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 23 Jan 2022 16:42:09 -0800 Subject: [PATCH 08/70] Fix path to VerifyNoBS. --- NetNewsWire.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index ff66384e0..c5cbddbc0 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -3590,7 +3590,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBuildSettings.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n"; + shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBS.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n"; }; 652832CF2683DD0400E6F37C /* Run Script: Delete Unnecessary Frameworks For Share Extension */ = { isa = PBXShellScriptBuildPhase; From 101d9ea0a3503220741f2ce551e00703a7de0ab9 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 23 Jan 2022 19:53:52 -0700 Subject: [PATCH 09/70] Change how we delete embedded frameworks --- NetNewsWire.xcodeproj/project.pbxproj | 76 +++++++-------------------- 1 file changed, 19 insertions(+), 57 deletions(-) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index c5cbddbc0..59b911fff 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -2876,7 +2876,6 @@ 176813F12564BB2C00D98635 /* Resources */, 1701E1BF25689B44009453D8 /* SwiftGen Localization */, 17EF6A1725C4E59D002C9F81 /* Embed Frameworks */, - 84BB8F7F26224B5F00DB61F8 /* Delete Unnecessary Frameworks */, ); buildRules = ( ); @@ -2920,7 +2919,6 @@ 51314634235A7BBE00387FDC /* Frameworks */, 51314635235A7BBE00387FDC /* Resources */, 5102AE7724D17FB50050839C /* Embed Frameworks */, - 513F328A2593EFCE0003048F /* Delete Unnecessary Frameworks */, ); buildRules = ( ); @@ -2944,7 +2942,6 @@ 513C5CE3232571C2003D4054 /* Frameworks */, 513C5CE4232571C2003D4054 /* Resources */, 5102AE7324D17FAA0050839C /* Embed Frameworks */, - 513F328B2593F03F0003048F /* Delete Unnecessary Frameworks */, ); buildRules = ( ); @@ -3082,6 +3079,7 @@ 51C451DF2264C7F200C03939 /* Embed Frameworks */, 513C5CF1232571C2003D4054 /* Embed App Extensions */, 515D50802326D02600EE1167 /* Run Script: Verify No Build Settings */, + 5170CA5A279E468000702605 /* Delete Unnecessary Frameworks */, ); buildRules = ( ); @@ -3538,42 +3536,6 @@ shellPath = /bin/sh; shellScript = "if ! command -v swiftgen &> /dev/null\nthen\n echo \"swiftgen could not be found\"\n exit\nfi\n\nswiftgen run strings -t structured-swift5 \"$PROJECT_DIR/Widget/Resources/en.lproj/Localizable.strings\" \"$PROJECT_DIR/Widget/Resources/Localizable.stringsdict\" --output \"$PROJECT_DIR/Widget/Resources/Localized.swift\";\n"; }; - 513F328A2593EFCE0003048F /* Delete Unnecessary Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Delete Unnecessary Frameworks"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Delete the framework that Xcode should have never included\n# https://forums.swift.org/t/is-this-an-xcode-bug-or-somehow-related-to-spm/33987\nrm -rf \"${TARGET_BUILD_DIR}/NetNewsWire iOS Intents Extension.appex/Frameworks\"\n"; - }; - 513F328B2593F03F0003048F /* Delete Unnecessary Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Delete Unnecessary Frameworks"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Delete the framework that Xcode should have never included\n# https://forums.swift.org/t/is-this-an-xcode-bug-or-somehow-related-to-spm/33987\nrm -rf \"${TARGET_BUILD_DIR}/NetNewsWire iOS Share Extension.appex/Frameworks\"\n"; - }; 515D50802326D02600EE1167 /* Run Script: Verify No Build Settings */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; @@ -3592,6 +3554,24 @@ shellPath = /bin/sh; shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBS.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n"; }; + 5170CA5A279E468000702605 /* Delete Unnecessary Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Delete Unnecessary Frameworks"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"${CODESIGNING_FOLDER_PATH}/PlugIns/\"\n\nfor plugin in *; do\n if [ -d \"$plugin\" ]; then\n if [ -d \"${plugin}/Frameworks\" ]; then\n rm -rf \"${plugin}/Frameworks\"\n fi\n fi\ndone\n\n# codesign for Debugging on device\nif [ \"${CONFIGURATION}\" == \"Debug\" ] & [ \"${SDKROOT}\" != *Simulator* ] ; then\n\n echo \"Code signing frameworks...\"\n find \"${CODESIGNING_FOLDER_PATH}/Frameworks\" -maxdepth 1 -name '*.framework' -print0 | while read -d $'\\0' framework\n do\n # only sign frameworks without a signature\n if ! codesign -v \"${framework}\"; then\n codesign --force --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" --preserve-metadata=identifier,entitlements --timestamp=none \"${framework}\"\n echo \"Added missing signature to '${framework}'\"\n fi\n done\nfi\n"; + }; 652832CF2683DD0400E6F37C /* Run Script: Delete Unnecessary Frameworks For Share Extension */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -3696,24 +3676,6 @@ shellPath = /bin/sh; shellScript = "# Remove unused Sparkle components\nSPARKLE_DIR=\"${CODESIGNING_FOLDER_PATH}/Contents/Frameworks/Sparkle.framework\"\nfind \"${SPARKLE_DIR}\" -name Updater.app -execdir rm -rf {} \\;\nfind \"${SPARKLE_DIR}\" -name Autoupdate -execdir rm -rf {} \\;\n\nPLUGINS_DIR=\"${CODESIGNING_FOLDER_PATH}/Contents/PlugIns\"\nXPC_DIR=\"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices\"\nfind \"${PLUGINS_DIR}\" -name Sparkle* -execdir rm -rf {} \\;\nfind \"${XPC_DIR}\" -name Sparkle* -execdir rm -rf {} \\;\n\n# Re-sign Sparkle after removing components\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/Frameworks/Sparkle.framework\"\n\n# Sign XPC Helpers and their internal binaries\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Updater.app\"\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc/Contents/MacOS/Autoupdate\"\ncodesign --verbose --entitlements \"${PROJECT_DIR}/submodules/Sparkle/Downloader/org.sparkle-project.Downloader.entitlements\" --force -o runtime --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.Downloader.xpc\"\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.InstallerLauncher.xpc\"\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.InstallerConnection.xpc\"\ncodesign --verbose --force -o runtime --preserve-metadata=entitlements --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" \"${CODESIGNING_FOLDER_PATH}/Contents/XPCServices/org.sparkle-project.InstallerStatus.xpc\"\n"; }; - 84BB8F7F26224B5F00DB61F8 /* Delete Unnecessary Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Delete Unnecessary Frameworks"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Delete the framework that Xcode should have never included\n# https://forums.swift.org/t/is-this-an-xcode-bug-or-somehow-related-to-spm/33987\nrm -rf \"${TARGET_BUILD_DIR}/NetNewsWire iOS Widget Extension.appex/Frameworks\"\n"; - }; 84C987A52000AC9E0066B150 /* Run Script: Automated build numbers */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; From 639ec3c757d498f1e542e85524fae1760c47ea15 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 23 Jan 2022 20:47:29 -0800 Subject: [PATCH 10/70] Bump build to 6101. --- Technotes/ReleaseNotes-iOS.markdown | 2 +- xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Technotes/ReleaseNotes-iOS.markdown b/Technotes/ReleaseNotes-iOS.markdown index ec358a074..a29a3dcbe 100644 --- a/Technotes/ReleaseNotes-iOS.markdown +++ b/Technotes/ReleaseNotes-iOS.markdown @@ -1,6 +1,6 @@ # iOS Release Notes -### 6.1 TestFlight build 6100 - 23 Jan 2022 +### 6.1 TestFlight build 6101 - 23 Jan 2022 Article themes. Several themes ship with the app, and you can create your own. You can change the theme in Preferences. Copy URLs using repaired, rather than raw, feed links. diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 3be188d1a..d46e9e76f 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 6.1 -CURRENT_PROJECT_VERSION = 6100 +CURRENT_PROJECT_VERSION = 6101 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From 9d41e8eec280d852ace82640fccc8dbc51f79c5d Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 23 Jan 2022 21:03:04 -0800 Subject: [PATCH 11/70] Make app owner of its document type. Bump build to 6102. --- Technotes/ReleaseNotes-iOS.markdown | 2 +- iOS/Resources/Info.plist | 2 ++ xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Technotes/ReleaseNotes-iOS.markdown b/Technotes/ReleaseNotes-iOS.markdown index a29a3dcbe..a1732684c 100644 --- a/Technotes/ReleaseNotes-iOS.markdown +++ b/Technotes/ReleaseNotes-iOS.markdown @@ -1,6 +1,6 @@ # iOS Release Notes -### 6.1 TestFlight build 6101 - 23 Jan 2022 +### 6.1 TestFlight build 6102 - 23 Jan 2022 Article themes. Several themes ship with the app, and you can create your own. You can change the theme in Preferences. Copy URLs using repaired, rather than raw, feed links. diff --git a/iOS/Resources/Info.plist b/iOS/Resources/Info.plist index 81f8b60cf..1b350b77f 100644 --- a/iOS/Resources/Info.plist +++ b/iOS/Resources/Info.plist @@ -204,6 +204,8 @@ CFBundleTypeName NetNewsWire Theme + LSHandlerRank + Owner CFBundleTypeRole Viewer LSItemContentTypes diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index d46e9e76f..731951dec 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 6.1 -CURRENT_PROJECT_VERSION = 6101 +CURRENT_PROJECT_VERSION = 6102 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From ac8fdabb3ee5b925d6ce3afa66face2e957ccd0f Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Tue, 25 Jan 2022 06:07:33 +0800 Subject: [PATCH 12/70] Removes 1Password --- NetNewsWire.xcodeproj/project.pbxproj | 14 - Shared/1Password/OnePasswordExtension.h | 220 ------ Shared/1Password/OnePasswordExtension.m | 638 ------------------ .../FeedWranglerAccountViewController.swift | 15 - .../FeedbinAccountViewController.swift | 19 - .../NewsBlurAccountViewController.swift | 15 - .../ReaderAPIAccountViewController.swift | 29 - .../1password.imageset/Contents.json | 23 - .../1password.imageset/onepassword-navbar.png | Bin 770 -> 0 bytes .../onepassword-navbar@2x.png | Bin 1702 -> 0 bytes .../onepassword-navbar@3x.png | Bin 2757 -> 0 bytes .../NetNewsWire-iOS-Bridging-Header.h | 2 +- 12 files changed, 1 insertion(+), 974 deletions(-) delete mode 100644 Shared/1Password/OnePasswordExtension.h delete mode 100644 Shared/1Password/OnePasswordExtension.m delete mode 100644 iOS/Resources/Assets.xcassets/1password.imageset/Contents.json delete mode 100644 iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png delete mode 100644 iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@2x.png delete mode 100644 iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 59b911fff..114d87fca 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -54,7 +54,6 @@ 17D0682C2564F47E00C0B37E /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 17D0682B2564F47E00C0B37E /* Localizable.stringsdict */; }; 17D643B126F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */; }; 17D643B226F8A436008D4C05 /* ArticleThemeDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */; }; - 17D7586F2679C21800B17787 /* OnePasswordExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D7586E2679C21800B17787 /* OnePasswordExtension.m */; }; 17E0084625941887000C23F0 /* SizeCategories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E0084525941887000C23F0 /* SizeCategories.swift */; }; 17EF6A2125C4E5B4002C9F81 /* RSWeb in Frameworks */ = {isa = PBXBuildFile; productRef = 17EF6A2025C4E5B4002C9F81 /* RSWeb */; }; 17EF6A2225C4E5B4002C9F81 /* RSWeb in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 17EF6A2025C4E5B4002C9F81 /* RSWeb */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; @@ -1149,8 +1148,6 @@ 17D0682B2564F47E00C0B37E /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; 17D643B026F8A436008D4C05 /* ArticleThemeDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeDownloader.swift; sourceTree = ""; }; 17D7586C2679C21700B17787 /* NetNewsWire-iOS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-iOS-Bridging-Header.h"; sourceTree = ""; }; - 17D7586D2679C21800B17787 /* OnePasswordExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OnePasswordExtension.h; sourceTree = ""; }; - 17D7586E2679C21800B17787 /* OnePasswordExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnePasswordExtension.m; sourceTree = ""; }; 17E0084525941887000C23F0 /* SizeCategories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCategories.swift; sourceTree = ""; }; 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = ""; }; 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = ""; }; @@ -1794,15 +1791,6 @@ path = Resources; sourceTree = ""; }; - 17D7586B2679C1DF00B17787 /* 1Password */ = { - isa = PBXGroup; - children = ( - 17D7586D2679C21800B17787 /* OnePasswordExtension.h */, - 17D7586E2679C21800B17787 /* OnePasswordExtension.m */, - ); - path = 1Password; - sourceTree = ""; - }; 510289CE2451BA1E00426DDF /* Twitter */ = { isa = PBXGroup; children = ( @@ -2594,7 +2582,6 @@ 510C43F5243D0325009F70C3 /* ExtensionPoints */, 511D43CE231FA51100FB1562 /* Resources */, 176813A22564B9D100D98635 /* Widget */, - 17D7586B2679C1DF00B17787 /* 1Password */, 173A64162547BE0900267F6E /* AccountType+Helpers.swift */, ); path = Shared; @@ -4152,7 +4139,6 @@ 8454C3F3263F2D8700E3F9C7 /* IconImageCache.swift in Sources */, B24E9ADE245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */, C5A6ED5223C9AF4300AB6BE2 /* TitleActivityItemSource.swift in Sources */, - 17D7586F2679C21800B17787 /* OnePasswordExtension.m in Sources */, 17071EF126F8137400F5E71D /* ArticleTheme+Notifications.swift in Sources */, 51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */, 84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */, diff --git a/Shared/1Password/OnePasswordExtension.h b/Shared/1Password/OnePasswordExtension.h deleted file mode 100644 index c8326d3ff..000000000 --- a/Shared/1Password/OnePasswordExtension.h +++ /dev/null @@ -1,220 +0,0 @@ -//Copyright (c) 2014-2020 AgileBits Inc. -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. - -#import -#import -#import - -#ifdef __IPHONE_8_0 -#import -#endif - -#if __has_feature(nullability) -NS_ASSUME_NONNULL_BEGIN -#else -#define nullable -#define __nullable -#define nonnull -#define __nonnull -#endif - -// Login Dictionary keys - Used to get or set the properties of a 1Password Login - -FOUNDATION_EXPORT NSString *const AppExtensionURLStringKey; -FOUNDATION_EXPORT NSString *const AppExtensionUsernameKey; -FOUNDATION_EXPORT NSString *const AppExtensionPasswordKey; -FOUNDATION_EXPORT NSString *const AppExtensionTOTPKey; -FOUNDATION_EXPORT NSString *const AppExtensionTitleKey; -FOUNDATION_EXPORT NSString *const AppExtensionNotesKey; -FOUNDATION_EXPORT NSString *const AppExtensionSectionTitleKey; -FOUNDATION_EXPORT NSString *const AppExtensionFieldsKey; -FOUNDATION_EXPORT NSString *const AppExtensionReturnedFieldsKey; -FOUNDATION_EXPORT NSString *const AppExtensionOldPasswordKey; -FOUNDATION_EXPORT NSString *const AppExtensionPasswordGeneratorOptionsKey; - -// Password Generator options - Used to set the 1Password Password Generator options when saving a new Login or when changing the password for for an existing Login -FOUNDATION_EXPORT NSString *const AppExtensionGeneratedPasswordMinLengthKey; -FOUNDATION_EXPORT NSString *const AppExtensionGeneratedPasswordMaxLengthKey; -FOUNDATION_EXPORT NSString *const AppExtensionGeneratedPasswordRequireDigitsKey; -FOUNDATION_EXPORT NSString *const AppExtensionGeneratedPasswordRequireSymbolsKey; -FOUNDATION_EXPORT NSString *const AppExtensionGeneratedPasswordForbiddenCharactersKey; - -// Errors codes -FOUNDATION_EXPORT NSString *const AppExtensionErrorDomain; - -FOUNDATION_EXPORT NS_ENUM(NSUInteger, AppExtensionErrorCode) { - AppExtensionErrorCodeCancelledByUser = 0, - AppExtensionErrorCodeAPINotAvailable = 1, - AppExtensionErrorCodeFailedToContactExtension = 2, - AppExtensionErrorCodeFailedToLoadItemProviderData = 3, - AppExtensionErrorCodeCollectFieldsScriptFailed = 4, - AppExtensionErrorCodeFillFieldsScriptFailed = 5, - AppExtensionErrorCodeUnexpectedData = 6, - AppExtensionErrorCodeFailedToObtainURLStringFromWebView = 7 -}; - -// Note to creators of libraries or frameworks: -// If you include this code within your library, then to prevent potential duplicate symbol -// conflicts for adopters of your library, you should rename the OnePasswordExtension class -// and associated typedefs. You might to so by adding your own project prefix, e.g., -// MyLibraryOnePasswordExtension. - -typedef void (^OnePasswordLoginDictionaryCompletionBlock)(NSDictionary * __nullable loginDictionary, NSError * __nullable error); -typedef void (^OnePasswordSuccessCompletionBlock)(BOOL success, NSError * __nullable error); -typedef void (^OnePasswordExtensionItemCompletionBlock)(NSExtensionItem * __nullable extensionItem, NSError * __nullable error); - -@interface OnePasswordExtension : NSObject - -+ (OnePasswordExtension *)sharedExtension; - -/*! - @discussion Determines if the 1Password Extension is available. Allows you to only show the 1Password login button to those - that can use it. Of course, you could leave the button enabled and educate users about the virtues of strong, unique - passwords instead :) - - @return isAppExtensionAvailable Returns YES if any app that supports the generic `org-appextension-feature-password-management` feature is installed on the device. - */ -#ifdef __IPHONE_8_0 -- (BOOL)isAppExtensionAvailable NS_EXTENSION_UNAVAILABLE_IOS("Not available in an extension. Check if org-appextension-feature-password-management:// URL can be opened by the app."); -#else -- (BOOL)isAppExtensionAvailable; -#endif - -/*! - Called from your login page, this method will find all available logins for the given URLString. - - @discussion 1Password will show all matching Login for the naked domain of the given URLString. For example if the user has an item in your 1Password vault with "subdomain1.domain.com” as the website and another one with "subdomain2.domain.com”, and the URLString is "https://domain.com", 1Password will show both items. - - However, if no matching login is found for "https://domain.com", the 1Password Extension will display the "Show all Logins" button so that the user can search among all the Logins in the vault. This is especially useful when the user has a login for "https://olddomain.com". - - After the user selects a login, it is stored into an NSDictionary and given to your completion handler. Use the `Login Dictionary keys` above to - extract the needed information and update your UI. The completion block is guaranteed to be called on the main thread. - - @param URLString For the matching Logins in the 1Password vault. - - @param viewController The view controller from which the 1Password Extension is invoked. Usually `self` - - @param sender The sender which triggers the share sheet to show. UIButton, UIBarButtonItem or UIView. Can also be nil on iPhone, but not on iPad. - - @param completion A completion block called with two parameters loginDictionary and error once completed. The loginDictionary reply parameter that contains the username, password and the One-Time Password if available. The error Reply parameter that is nil if the 1Password Extension has been successfully completed, or it contains error information about the completion failure. - */ -- (void)findLoginForURLString:(nonnull NSString *)URLString forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion; - -/*! - Create a new login within 1Password and allow the user to generate a new password before saving. - - @discussion The provided URLString should be unique to your app or service and be identical to what you pass into the find login method. - The completion block is guaranteed to be called on the main - thread. - - @param URLString For the new Login to be saved in 1Password. - - @param loginDetailsDictionary about the Login to be saved, including custom fields, are stored in an dictionary and given to the 1Password Extension. - - @param passwordGenerationOptions The Password generator options represented in a dictionary form. - - @param viewController The view controller from which the 1Password Extension is invoked. Usually `self` - - @param sender The sender which triggers the share sheet to show. UIButton, UIBarButtonItem or UIView. Can also be nil on iPhone, but not on iPad. - - @param completion A completion block which is called with type parameters loginDictionary and error. The loginDictionary reply parameter which contain all the information about the newly saved Login. Use the `Login Dictionary keys` above to extract the needed information and update your UI. For example, updating the UI with the newly generated password lets the user know their action was successful. The error reply parameter that is nil if the 1Password Extension has been successfully completed, or it contains error information about the completion failure. - */ -- (void)storeLoginForURLString:(nonnull NSString *)URLString loginDetails:(nullable NSDictionary *)loginDetailsDictionary passwordGenerationOptions:(nullable NSDictionary *)passwordGenerationOptions forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion; - -/*! - Change the password for an existing login within 1Password. - - @discussion The provided URLString should be unique to your app or service and be identical to what you pass into the find login method. The completion block is guaranteed to be called on the main thread. - - 1Password 6 and later: - The 1Password Extension will display all available the matching Logins for the given URL string. The user can choose which Login item to update. The "New Login" button will also be available at all times, in case the user wishes to to create a new Login instead, - - 1Password 5: - These are the three scenarios that are supported: - 1. A single matching Login is found: 1Password will enter edit mode for that Login and will update its password using the value for AppExtensionPasswordKey. - 2. More than a one matching Logins are found: 1Password will display a list of all matching Logins. The user must choose which one to update. Once in edit mode, the Login will be updated with the new password. - 3. No matching login is found: 1Password will create a new Login using the optional fields if available to populate its properties. - - @param URLString for the Login to be updated with a new password in 1Password. - - @param loginDetailsDictionary about the Login to be saved, including old password and the username, are stored in an dictionary and given to the 1Password Extension. - - @param passwordGenerationOptions The Password generator options represented in a dictionary form. - - @param viewController The view controller from which the 1Password Extension is invoked. Usually `self` - - @param sender The sender which triggers the share sheet to show. UIButton, UIBarButtonItem or UIView. Can also be nil on iPhone, but not on iPad. - - @param completion A completion block which is called with type parameters loginDictionary and error. The loginDictionary reply parameter which contain all the information about the newly updated Login, including the newly generated and the old password. Use the `Login Dictionary keys` above to extract the needed information and update your UI. For example, updating the UI with the newly generated password lets the user know their action was successful. The error reply parameter that is nil if the 1Password Extension has been successfully completed, or it contains error information about the completion failure. - */ -- (void)changePasswordForLoginForURLString:(nonnull NSString *)URLString loginDetails:(nullable NSDictionary *)loginDetailsDictionary passwordGenerationOptions:(nullable NSDictionary *)passwordGenerationOptions forViewController:(UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion; - -/*! - Called from your web view controller, this method will show all the saved logins for the active page in the provided web - view, and automatically fill the HTML form fields. Supports WKWebView. - - @discussion 1Password will show all matching Login for the naked domain of the current website. For example if the user has an item in your 1Password vault with "subdomain1.domain.com” as the website and another one with "subdomain2.domain.com”, and the current website is "https://domain.com", 1Password will show both items. - - However, if no matching login is found for "https://domain.com", the 1Password Extension will display the "New Login" button so that the user can create a new Login for the current website. - - @param webView The web view which displays the form to be filled. The active WKWebView. Must not be nil. - - @param viewController The view controller from which the 1Password Extension is invoked. Usually `self` - - @param sender The sender which triggers the share sheet to show. UIButton, UIBarButtonItem or UIView. Can also be nil on iPhone, but not on iPad. - - @param yesOrNo Boolean flag. If YES is passed only matching Login items will be shown, otherwise the 1Password Extension will also display Credit Cards and Identities. - - @param completion Completion block called on completion with parameters success, and error. The success reply parameter that is YES if the 1Password Extension has been successfully completed or NO otherwise. The error reply parameter that is nil if the 1Password Extension has been successfully completed, or it contains error information about the completion failure. - */ -- (void)fillItemIntoWebView:(nonnull WKWebView *)webView forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion; - -/*! - Called in the UIActivityViewController completion block to find out whether or not the user selected the 1Password Extension activity. - - @param activityType or the bundle identifier of the selected activity in the share sheet. - - @return isOnePasswordExtensionActivityType Returns YES if the selected activity is the 1Password extension, NO otherwise. - */ -- (BOOL)isOnePasswordExtensionActivityType:(nullable NSString *)activityType; - -/*! - The returned NSExtensionItem can be used to create your own UIActivityViewController. Use `isOnePasswordExtensionActivityType:` and `fillReturnedItems:intoWebView:completion:` in the activity view controller completion block to process the result. The completion block is guaranteed to be called on the main thread. - - @param webView The web view which displays the form to be filled. The active WKWebView. Must not be nil. - - @param completion Completion block called on completion with extensionItem and error. The extensionItem reply parameter that is contains all the info required by the 1Password extension if has been successfully completed or nil otherwise. The error reply parameter that is nil if the 1Password extension item has been successfully created, or it contains error information about the completion failure. - */ -- (void)createExtensionItemForWebView:(nonnull WKWebView *)webView completion:(nonnull OnePasswordExtensionItemCompletionBlock)completion; - -/*! - Method used in the UIActivityViewController completion block to fill information into a web view. - - @param returnedItems Array which contains the selected activity in the share sheet. Empty array if the share sheet is cancelled by the user. - @param webView The web view which displays the form to be filled. The active WKWebView. Must not be nil. - - @param completion Completion block called on completion with parameters success, and error. The success reply parameter that is YES if the 1Password Extension has been successfully completed or NO otherwise. The error reply parameter that is nil if the 1Password Extension has been successfully completed, or it contains error information about the completion failure. - */ -- (void)fillReturnedItems:(nullable NSArray *)returnedItems intoWebView:(nonnull WKWebView *)webView completion:(nonnull OnePasswordSuccessCompletionBlock)completion; -@end - -#if __has_feature(nullability) -NS_ASSUME_NONNULL_END -#endif diff --git a/Shared/1Password/OnePasswordExtension.m b/Shared/1Password/OnePasswordExtension.m deleted file mode 100644 index 5f8531632..000000000 --- a/Shared/1Password/OnePasswordExtension.m +++ /dev/null @@ -1,638 +0,0 @@ -//Copyright (c) 2014-2020 AgileBits Inc. -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. - -#import "OnePasswordExtension.h" - -NSString *const AppExtensionURLStringKey = @"url_string"; -NSString *const AppExtensionUsernameKey = @"username"; -NSString *const AppExtensionPasswordKey = @"password"; -NSString *const AppExtensionTOTPKey = @"totp"; -NSString *const AppExtensionTitleKey = @"login_title"; -NSString *const AppExtensionNotesKey = @"notes"; -NSString *const AppExtensionSectionTitleKey = @"section_title"; -NSString *const AppExtensionFieldsKey = @"fields"; -NSString *const AppExtensionReturnedFieldsKey = @"returned_fields"; -NSString *const AppExtensionOldPasswordKey = @"old_password"; -NSString *const AppExtensionPasswordGeneratorOptionsKey = @"password_generator_options"; - -NSString *const AppExtensionGeneratedPasswordMinLengthKey = @"password_min_length"; -NSString *const AppExtensionGeneratedPasswordMaxLengthKey = @"password_max_length"; -NSString *const AppExtensionGeneratedPasswordRequireDigitsKey = @"password_require_digits"; -NSString *const AppExtensionGeneratedPasswordRequireSymbolsKey = @"password_require_symbols"; -NSString *const AppExtensionGeneratedPasswordForbiddenCharactersKey = @"password_forbidden_characters"; - -NSString *const AppExtensionErrorDomain = @"OnePasswordExtension"; - -// Version -#define VERSION_NUMBER @(185) -static NSString *const AppExtensionVersionNumberKey = @"version_number"; - -// Available App Extension Actions -static NSString *const kUTTypeAppExtensionFindLoginAction = @"org.appextension.find-login-action"; -static NSString *const kUTTypeAppExtensionSaveLoginAction = @"org.appextension.save-login-action"; -static NSString *const kUTTypeAppExtensionChangePasswordAction = @"org.appextension.change-password-action"; -static NSString *const kUTTypeAppExtensionFillWebViewAction = @"org.appextension.fill-webview-action"; -static NSString *const kUTTypeAppExtensionFillBrowserAction = @"org.appextension.fill-browser-action"; - -// WebView Dictionary keys -static NSString *const AppExtensionWebViewPageFillScript = @"fillScript"; -static NSString *const AppExtensionWebViewPageDetails = @"pageDetails"; - -@implementation OnePasswordExtension - -#pragma mark - Public Methods - -+ (OnePasswordExtension *)sharedExtension { - static dispatch_once_t onceToken; - static OnePasswordExtension *__sharedExtension; - - dispatch_once(&onceToken, ^{ - __sharedExtension = [OnePasswordExtension new]; - }); - - return __sharedExtension; -} - -- (BOOL)isAppExtensionAvailable { - if ([self isSystemAppExtensionAPIAvailable]) { - return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"org-appextension-feature-password-management://"]]; - } - - return NO; -} - -#pragma mark - Native app Login - -- (void)findLoginForURLString:(nonnull NSString *)URLString forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion { - NSAssert(URLString != nil, @"URLString must not be nil"); - NSAssert(viewController != nil, @"viewController must not be nil"); - - if (NO == [self isSystemAppExtensionAPIAvailable]) { - NSLog(@"Failed to findLoginForURLString, system API is not available"); - if (completion) { - completion(nil, [OnePasswordExtension systemAppExtensionAPINotAvailableError]); - } - - return; - } - - NSDictionary *item = @{ AppExtensionVersionNumberKey: VERSION_NUMBER, AppExtensionURLStringKey: URLString }; - - UIActivityViewController *activityViewController = [self activityViewControllerForItem:item viewController:viewController sender:sender typeIdentifier:kUTTypeAppExtensionFindLoginAction]; - activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - if (returnedItems.count == 0) { - NSError *error = nil; - if (activityError) { - NSLog(@"Failed to findLoginForURLString: %@", activityError); - error = [OnePasswordExtension failedToContactExtensionErrorWithActivityError:activityError]; - } - else { - error = [OnePasswordExtension extensionCancelledByUserError]; - } - - if (completion) { - completion(nil, error); - } - - return; - } - - [self processExtensionItem:returnedItems.firstObject completion:^(NSDictionary *itemDictionary, NSError *error) { - if (completion) { - completion(itemDictionary, error); - } - }]; - }; - - [viewController presentViewController:activityViewController animated:YES completion:nil]; -} - -#pragma mark - New User Registration - -- (void)storeLoginForURLString:(nonnull NSString *)URLString loginDetails:(nullable NSDictionary *)loginDetailsDictionary passwordGenerationOptions:(nullable NSDictionary *)passwordGenerationOptions forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion { - NSAssert(URLString != nil, @"URLString must not be nil"); - NSAssert(viewController != nil, @"viewController must not be nil"); - - if (NO == [self isSystemAppExtensionAPIAvailable]) { - NSLog(@"Failed to storeLoginForURLString, system API is not available"); - if (completion) { - completion(nil, [OnePasswordExtension systemAppExtensionAPINotAvailableError]); - } - - return; - } - - NSMutableDictionary *newLoginAttributesDict = [NSMutableDictionary new]; - newLoginAttributesDict[AppExtensionVersionNumberKey] = VERSION_NUMBER; - newLoginAttributesDict[AppExtensionURLStringKey] = URLString; - [newLoginAttributesDict addEntriesFromDictionary:loginDetailsDictionary]; - if (passwordGenerationOptions.count > 0) { - newLoginAttributesDict[AppExtensionPasswordGeneratorOptionsKey] = passwordGenerationOptions; - } - - UIActivityViewController *activityViewController = [self activityViewControllerForItem:newLoginAttributesDict viewController:viewController sender:sender typeIdentifier:kUTTypeAppExtensionSaveLoginAction]; - activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - if (returnedItems.count == 0) { - NSError *error = nil; - if (activityError) { - NSLog(@"Failed to storeLoginForURLString: %@", activityError); - error = [OnePasswordExtension failedToContactExtensionErrorWithActivityError:activityError]; - } - else { - error = [OnePasswordExtension extensionCancelledByUserError]; - } - - if (completion) { - completion(nil, error); - } - - return; - } - - [self processExtensionItem:returnedItems.firstObject completion:^(NSDictionary *itemDictionary, NSError *error) { - if (completion) { - completion(itemDictionary, error); - } - }]; - }; - - [viewController presentViewController:activityViewController animated:YES completion:nil]; -} - -#pragma mark - Change Password - -- (void)changePasswordForLoginForURLString:(nonnull NSString *)URLString loginDetails:(nullable NSDictionary *)loginDetailsDictionary passwordGenerationOptions:(nullable NSDictionary *)passwordGenerationOptions forViewController:(UIViewController *)viewController sender:(nullable id)sender completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion { - NSAssert(URLString != nil, @"URLString must not be nil"); - NSAssert(viewController != nil, @"viewController must not be nil"); - - if (NO == [self isSystemAppExtensionAPIAvailable]) { - NSLog(@"Failed to changePasswordForLoginWithUsername, system API is not available"); - if (completion) { - completion(nil, [OnePasswordExtension systemAppExtensionAPINotAvailableError]); - } - - return; - } - - NSMutableDictionary *item = [NSMutableDictionary new]; - item[AppExtensionVersionNumberKey] = VERSION_NUMBER; - item[AppExtensionURLStringKey] = URLString; - [item addEntriesFromDictionary:loginDetailsDictionary]; - if (passwordGenerationOptions.count > 0) { - item[AppExtensionPasswordGeneratorOptionsKey] = passwordGenerationOptions; - } - - UIActivityViewController *activityViewController = [self activityViewControllerForItem:item viewController:viewController sender:sender typeIdentifier:kUTTypeAppExtensionChangePasswordAction]; - - activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - if (returnedItems.count == 0) { - NSError *error = nil; - if (activityError) { - NSLog(@"Failed to changePasswordForLoginWithUsername: %@", activityError); - error = [OnePasswordExtension failedToContactExtensionErrorWithActivityError:activityError]; - } - else { - error = [OnePasswordExtension extensionCancelledByUserError]; - } - - if (completion) { - completion(nil, error); - } - - return; - } - - [self processExtensionItem:returnedItems.firstObject completion:^(NSDictionary *itemDictionary, NSError *error) { - if (completion) { - completion(itemDictionary, error); - } - }]; - }; - - [viewController presentViewController:activityViewController animated:YES completion:nil]; -} - -#pragma mark - Web View filling Support - -- (void)fillItemIntoWebView:(nonnull WKWebView *)webView forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion { - NSAssert(webView != nil, @"webView must not be nil"); - NSAssert(viewController != nil, @"viewController must not be nil"); - NSAssert([webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView."); - - [self fillItemIntoWKWebView:webView forViewController:viewController sender:(id)sender showOnlyLogins:yesOrNo completion:^(BOOL success, NSError *error) { - if (completion) { - completion(success, error); - } - }]; -} - -#pragma mark - Support for custom UIActivityViewControllers - -- (BOOL)isOnePasswordExtensionActivityType:(nullable NSString *)activityType { - return [@"com.agilebits.onepassword-ios.extension" isEqualToString:activityType] || [@"com.agilebits.beta.onepassword-ios.extension" isEqualToString:activityType]; -} - -- (void)createExtensionItemForWebView:(nonnull WKWebView *)webView completion:(nonnull OnePasswordExtensionItemCompletionBlock)completion { - NSAssert(webView != nil, @"webView must not be nil"); - NSAssert([webView isKindOfClass:[WKWebView class]], @"webView must be an instance of WKWebView."); - - [webView evaluateJavaScript:OPWebViewCollectFieldsScript completionHandler:^(NSString *result, NSError *evaluateError) { - if (result == nil) { - NSLog(@"1Password Extension failed to collect web page fields: %@", evaluateError); - NSError *failedToCollectFieldsError = [OnePasswordExtension failedToCollectFieldsErrorWithUnderlyingError:evaluateError]; - if (completion) { - if ([NSThread isMainThread]) { - completion(nil, failedToCollectFieldsError); - } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(nil, failedToCollectFieldsError); - }); - } - } - - return; - } - - [self createExtensionItemForURLString:webView.URL.absoluteString webPageDetails:result completion:completion]; - }]; -} - -- (void)fillReturnedItems:(nullable NSArray *)returnedItems intoWebView:(nonnull WKWebView *)webView completion:(nonnull OnePasswordSuccessCompletionBlock)completion { - NSAssert(webView != nil, @"webView must not be nil"); - - if (returnedItems.count == 0) { - NSError *error = [OnePasswordExtension extensionCancelledByUserError]; - if (completion) { - completion(NO, error); - } - - return; - } - - [self processExtensionItem:returnedItems.firstObject completion:^(NSDictionary *itemDictionary, NSError *error) { - if (itemDictionary.count == 0) { - if (completion) { - completion(NO, error); - } - - return; - } - - NSString *fillScript = itemDictionary[AppExtensionWebViewPageFillScript]; - [self executeFillScript:fillScript inWebView:webView completion:^(BOOL success, NSError *executeFillScriptError) { - if (completion) { - completion(success, executeFillScriptError); - } - }]; - }]; -} - -#pragma mark - Private methods - -- (BOOL)isSystemAppExtensionAPIAvailable { - return [NSExtensionItem class] != nil; -} - -- (void)findLoginIn1PasswordWithURLString:(nonnull NSString *)URLString collectedPageDetails:(nullable NSString *)collectedPageDetails forWebViewController:(nonnull UIViewController *)forViewController sender:(nullable id)sender withWebView:(nonnull WKWebView *)webView showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion { - if ([URLString length] == 0) { - NSError *URLStringError = [OnePasswordExtension failedToObtainURLStringFromWebViewError]; - NSLog(@"Failed to findLoginIn1PasswordWithURLString: %@", URLStringError); - if (completion) { - completion(NO, URLStringError); - } - return; - } - - NSError *jsonError = nil; - NSData *data = [collectedPageDetails dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *collectedPageDetailsDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; - - if (collectedPageDetailsDictionary.count == 0) { - NSLog(@"Failed to parse JSON collected page details: %@", jsonError); - if (completion) { - completion(NO, jsonError); - } - return; - } - - NSDictionary *item = @{ AppExtensionVersionNumberKey : VERSION_NUMBER, AppExtensionURLStringKey : URLString, AppExtensionWebViewPageDetails : collectedPageDetailsDictionary }; - - NSString *typeIdentifier = yesOrNo ? kUTTypeAppExtensionFillWebViewAction : kUTTypeAppExtensionFillBrowserAction; - UIActivityViewController *activityViewController = [self activityViewControllerForItem:item viewController:forViewController sender:sender typeIdentifier:typeIdentifier]; - activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - if (returnedItems.count == 0) { - NSError *error = nil; - if (activityError) { - NSLog(@"Failed to findLoginIn1PasswordWithURLString: %@", activityError); - error = [OnePasswordExtension failedToContactExtensionErrorWithActivityError:activityError]; - } - else { - error = [OnePasswordExtension extensionCancelledByUserError]; - } - - if (completion) { - completion(NO, error); - } - - return; - } - - [self processExtensionItem:returnedItems.firstObject completion:^(NSDictionary *itemDictionary, NSError *processExtensionItemError) { - if (itemDictionary.count == 0) { - if (completion) { - completion(NO, processExtensionItemError); - } - - return; - } - - NSString *fillScript = itemDictionary[AppExtensionWebViewPageFillScript]; - [self executeFillScript:fillScript inWebView:webView completion:^(BOOL success, NSError *executeFillScriptError) { - if (completion) { - completion(success, executeFillScriptError); - } - }]; - }]; - }; - - [forViewController presentViewController:activityViewController animated:YES completion:nil]; -} - -- (void)fillItemIntoWKWebView:(nonnull WKWebView *)webView forViewController:(nonnull UIViewController *)viewController sender:(nullable id)sender showOnlyLogins:(BOOL)yesOrNo completion:(nonnull OnePasswordSuccessCompletionBlock)completion { - [webView evaluateJavaScript:OPWebViewCollectFieldsScript completionHandler:^(NSString *result, NSError *error) { - if (result == nil) { - NSLog(@"1Password Extension failed to collect web page fields: %@", error); - if (completion) { - completion(NO,[OnePasswordExtension failedToCollectFieldsErrorWithUnderlyingError:error]); - } - - return; - } - - [self findLoginIn1PasswordWithURLString:webView.URL.absoluteString collectedPageDetails:result forWebViewController:viewController sender:sender withWebView:webView showOnlyLogins:yesOrNo completion:^(BOOL success, NSError *findLoginError) { - if (completion) { - completion(success, findLoginError); - } - }]; - }]; -} - -- (void)executeFillScript:(NSString * __nullable)fillScript inWebView:(nonnull WKWebView *)webView completion:(nonnull OnePasswordSuccessCompletionBlock)completion { - - if (fillScript == nil) { - NSLog(@"Failed to executeFillScript, fillScript is missing"); - if (completion) { - completion(NO, [OnePasswordExtension failedToFillFieldsErrorWithLocalizedErrorMessage:NSLocalizedStringFromTable(@"Failed to fill web page because script is missing", @"OnePasswordExtension", @"1Password Extension Error Message") underlyingError:nil]); - } - - return; - } - - NSMutableString *scriptSource = [OPWebViewFillScript mutableCopy]; - [scriptSource appendFormat:@"(document, %@, undefined);", fillScript]; - - [webView evaluateJavaScript:scriptSource completionHandler:^(NSString *result, NSError *evaluationError) { - BOOL success = (result != nil); - NSError *error = nil; - - if (!success) { - NSLog(@"Cannot executeFillScript, evaluateJavaScript failed: %@", evaluationError); - error = [OnePasswordExtension failedToFillFieldsErrorWithLocalizedErrorMessage:NSLocalizedStringFromTable(@"Failed to fill web page because script could not be evaluated", @"OnePasswordExtension", @"1Password Extension Error Message") underlyingError:error]; - } - - if (completion) { - completion(success, error); - } - }]; -} - -- (void)processExtensionItem:(nullable NSExtensionItem *)extensionItem completion:(nonnull OnePasswordLoginDictionaryCompletionBlock)completion { - if (extensionItem.attachments.count == 0) { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Unexpected data returned by App Extension: extension item had no attachments." }; - NSError *error = [[NSError alloc] initWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeUnexpectedData userInfo:userInfo]; - if (completion) { - completion(nil, error); - } - return; - } - - NSItemProvider *itemProvider = extensionItem.attachments.firstObject; - if (NO == [itemProvider hasItemConformingToTypeIdentifier:(__bridge NSString *)kUTTypePropertyList]) { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Unexpected data returned by App Extension: extension item attachment does not conform to kUTTypePropertyList type identifier" }; - NSError *error = [[NSError alloc] initWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeUnexpectedData userInfo:userInfo]; - if (completion) { - completion(nil, error); - } - return; - } - - - [itemProvider loadItemForTypeIdentifier:(__bridge NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *itemDictionary, NSError *itemProviderError) { - NSError *error = nil; - if (itemDictionary.count == 0) { - NSLog(@"Failed to loadItemForTypeIdentifier: %@", itemProviderError); - error = [OnePasswordExtension failedToLoadItemProviderDataErrorWithUnderlyingError:itemProviderError]; - } - - if (completion) { - if ([NSThread isMainThread]) { - completion(itemDictionary, error); - } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(itemDictionary, error); - }); - } - } - }]; -} - -- (UIActivityViewController *)activityViewControllerForItem:(nonnull NSDictionary *)item viewController:(nonnull UIViewController*)viewController sender:(nullable id)sender typeIdentifier:(nonnull NSString *)typeIdentifier { - NSAssert(NO == (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad && sender == nil), @"sender must not be nil on iPad."); - - NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem:item typeIdentifier:typeIdentifier]; - - NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init]; - extensionItem.attachments = @[ itemProvider ]; - - UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:@[ extensionItem ] applicationActivities:nil]; - - if ([sender isKindOfClass:[UIBarButtonItem class]]) { - controller.popoverPresentationController.barButtonItem = sender; - } - else if ([sender isKindOfClass:[UIView class]]) { - controller.popoverPresentationController.sourceView = [sender superview]; - controller.popoverPresentationController.sourceRect = [sender frame]; - } - else { - NSLog(@"sender can be nil on iPhone"); - } - - return controller; -} - -- (void)createExtensionItemForURLString:(nonnull NSString *)URLString webPageDetails:(nullable NSString *)webPageDetails completion:(nonnull OnePasswordExtensionItemCompletionBlock)completion { - NSError *jsonError = nil; - NSData *data = [webPageDetails dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *webPageDetailsDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; - - if (webPageDetailsDictionary.count == 0) { - NSLog(@"Failed to parse JSON collected page details: %@", jsonError); - if (completion) { - completion(nil, jsonError); - } - return; - } - - NSDictionary *item = @{ AppExtensionVersionNumberKey : VERSION_NUMBER, AppExtensionURLStringKey : URLString, AppExtensionWebViewPageDetails : webPageDetailsDictionary }; - - NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem:item typeIdentifier:kUTTypeAppExtensionFillBrowserAction]; - - NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init]; - extensionItem.attachments = @[ itemProvider ]; - - if (completion) { - if ([NSThread isMainThread]) { - completion(extensionItem, nil); - } - else { - dispatch_async(dispatch_get_main_queue(), ^{ - completion(extensionItem, nil); - }); - } - } -} - -#pragma mark - Errors - -+ (NSError *)systemAppExtensionAPINotAvailableError { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"App Extension API is not available in this version of iOS", @"OnePasswordExtension", @"1Password Extension Error Message") }; - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeAPINotAvailable userInfo:userInfo]; -} - - -+ (NSError *)extensionCancelledByUserError { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"1Password Extension was cancelled by the user", @"OnePasswordExtension", @"1Password Extension Error Message") }; - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeCancelledByUser userInfo:userInfo]; -} - -+ (NSError *)failedToContactExtensionErrorWithActivityError:(nullable NSError *)activityError { - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - userInfo[NSLocalizedDescriptionKey] = NSLocalizedStringFromTable(@"Failed to contact the 1Password Extension", @"OnePasswordExtension", @"1Password Extension Error Message"); - if (activityError) { - userInfo[NSUnderlyingErrorKey] = activityError; - } - - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeFailedToContactExtension userInfo:userInfo]; -} - -+ (NSError *)failedToCollectFieldsErrorWithUnderlyingError:(nullable NSError *)underlyingError { - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - userInfo[NSLocalizedDescriptionKey] = NSLocalizedStringFromTable(@"Failed to execute script that collects web page information", @"OnePasswordExtension", @"1Password Extension Error Message"); - if (underlyingError) { - userInfo[NSUnderlyingErrorKey] = underlyingError; - } - - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeCollectFieldsScriptFailed userInfo:userInfo]; -} - -+ (NSError *)failedToFillFieldsErrorWithLocalizedErrorMessage:(nullable NSString *)errorMessage underlyingError:(nullable NSError *)underlyingError { - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - if (errorMessage) { - userInfo[NSLocalizedDescriptionKey] = errorMessage; - } - if (underlyingError) { - userInfo[NSUnderlyingErrorKey] = underlyingError; - } - - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeFillFieldsScriptFailed userInfo:userInfo]; -} - -+ (NSError *)failedToLoadItemProviderDataErrorWithUnderlyingError:(nullable NSError *)underlyingError { - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - userInfo[NSLocalizedDescriptionKey] = NSLocalizedStringFromTable(@"Failed to parse information returned by 1Password Extension", @"OnePasswordExtension", @"1Password Extension Error Message"); - if (underlyingError) { - userInfo[NSUnderlyingErrorKey] = underlyingError; - } - - return [[NSError alloc] initWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeFailedToLoadItemProviderData userInfo:userInfo]; -} - -+ (NSError *)failedToObtainURLStringFromWebViewError { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : NSLocalizedStringFromTable(@"Failed to obtain URL String from web view. The web view must be loaded completely when calling the 1Password Extension", @"OnePasswordExtension", @"1Password Extension Error Message") }; - return [NSError errorWithDomain:AppExtensionErrorDomain code:AppExtensionErrorCodeFailedToObtainURLStringFromWebView userInfo:userInfo]; -} - -#pragma mark - WebView field collection and filling scripts - -static NSString *const OPWebViewCollectFieldsScript = @";(function(document, undefined) {\ -\ - document.addEventListener('input',function(b){!1!==b.isTrusted&&'input'===b.target.tagName.toLowerCase()&&(b.target.dataset['com.agilebits.onepassword.userEdited']='yes')},!0);\ -(function(b,a,c){a.FieldCollector=new function(){function f(d){return d?d.toString().toLowerCase():''}function e(d,b,a,e){e!==c&&e===a||null===a||a===c||(d[b]=a)}function k(d,b){var a=[];try{a=d.querySelectorAll(b)}catch(J){console.error('[COLLECT FIELDS] @ag_querySelectorAll Exception in selector \"'+b+'\"')}return a}function m(d){var a,c=[];if(d.labels&&d.labels.length&&0=a.cells.length)return null;d=r(a.cells[d.cellIndex]);return d=l(d)}function p(a){return a.options?(a=Array.prototype.slice.call(a.options).map(function(a){var d=a.text,d=d?f(d).replace(/\\s/mg,'').replace(/[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\/?]/mg,\ -''):null;return[d?d:null,a.value]}),{options:a}):null}function F(a){switch(f(a.type)){case 'checkbox':return a.checked?'✓':'';case 'hidden':a=a.value;if(!a||'number'!=typeof a.length)return'';254b.clientWidth||10>b.clientHeight)return!1;var n=b.getClientRects();if(0===n.length)return!1;for(var p=0;pf||0>m.right)return!1;if(0>k||k>f||0>a||a>e)return!1;for(c=b.ownerDocument.elementFromPoint(k+(c.right>window.innerWidth?(window.innerWidth-k)/2:c.width/2),a+(c.bottom>window.innerHeight?\ -(window.innerHeight-a)/2:c.height/2));c&&c!==b&&c!==document;){if(c.tagName&&'string'===typeof c.tagName&&'label'===c.tagName.toLowerCase()&&b.labels&&0 URL? { switch accountType { case .freshRSS: diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json b/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json deleted file mode 100644 index 4bf18a9e4..000000000 --- a/iOS/Resources/Assets.xcassets/1password.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "onepassword-navbar.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "onepassword-navbar@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "onepassword-navbar@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png b/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar.png deleted file mode 100644 index d20bc7dfd2796a64fec6a7363c6d576017486d99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 770 zcmV+d1O5DoP)Px%yh%hsR7ee#l|640K@f&_?-C~gQJMJeoW>Cyoe>gEYzZMm84w*GB?u}I5}`E( zLIfgAhs1w?7z!GYkduOjKu1B4+{9-mSfYXOA>Z1~X`M!H8KpMRRgo*c@2@aN6>s>&TQ-w9t!(=NmOOnM&#DT7nzF1G zW14Xm#{#w~Y->Rf+-WwOZ{kdY8k^mgvz}+NuKOkHI2W=?9TIwJHL){3?%6Dv zNQ{Z$jlh^izuA1&GmbKHu}`_|C3tQ+j&n+U#uGG&2a>Wtf5w=fB_OPCO|AD*;XVKyaGo1$#MnvX> zZkbJ-h^P}qh-<<-hEtl}NjIB%Lwlb&{>CKP+wfzS$9$Hw^&UKF!qHM3zrDVn!mNRDq%70AXwl8 zN2qT1`nb_&JpKFZe3pl_AiSNzC54{Vp0L1e;(2v+TBRt{*CDPx*U`a$lRA>d&noVp}RTRhHeKW09sWDC4ndwF#YJxE_iVHUiF)j!iDSifHB5Q<= zG!TPgRgB>yEF|I3^L>2->2!LaTCEOXP8%s2#GLL9%K-5hZT4eEKZNU|^2-744rH^38EmLVfYy1m zb;5Dov-y1fLQP$R3}$v8E4H1W*!k|_()NZ`Jve>I>Hq7iZsA%}vU zLwuyHP#eiEwE-0-`4*K28P}5~;OEQbN1`+zHJF>rX1}4YXC4Up>i&KHYkB0!iT zcOaLmm`pu7na@X!X}hq}&$~)}Ou?;Qq1N9{77E+oYi!=NJ$yzsq6ICQZVWmsqNzM) z_$3?k6^33|8Ks_LGVh0l)&Q3yujcd+^Qf=su)LP!1w700t`Wux>;v25CNDGyT(Lop z*h)5H1Dm!@+reNS!>HVvCwGWovu0+2%Z*6Ig93t8nvv5(_*G20TrTedi+ALLcq3YW z(cm$_JLMWTh0OG5tB*V*I}V+mo-P3Rxaot*ya(SQc@H?oFAmc>DPV>Lezvict!CmP zlFQD!H^8_)m&-jZcs$?~$Z@lfwEiH$jfiZ0$%tqrl`|X*_|VGK+QTZq(=3O$rV9z* z!>}=^oL!sS1s>yiSko>BzX`_p(-UzWCNnwq#9!?KUn~}XM=2Nmv3fA=M_vEr zvQUO=YJeo6KKPwU`ZnyNR1n)1F%|1Xi9g*?K%E9?LL9SGb(%C&Ww8NzgUd}RZ$vN2 zBf#)+sSo-mZ1AqQeV5Z7J72^GX3@AjY`r8e7sJP-;?A`Vv|!=p^j=iLWA8lz+`Pse z^W2ikm|_fLknC+4<{8| zx1Ulwn*r_1mY9r}aJk1_E}id80cZN6vte7crGG&e4cp$XmL)-SRDD4EQ7>)2?eVv0nsh zS%Aw;wCKPnwFi&!_9O!|>LcJAwF%ev_V#w8*j={VceXs>sQ#Sne5piumfv36aFv;k zRB9i1`UD0T|HSQ8;}HiBx(+f5~YqFmRSuEfm2_G56|8sJhv z1Zh8;U{rXtE~fG+;GdQJN+eeK1tMyv>{tXnIXooLUy}sb4Jj(j%o+_NK;n6Jxo+s# zT!Dm{-$q;3JW*h*Ftazj+;YsSCa{m7uUNKMS+-(x7;Ag;Wg?4b`Vr$XDPXnrV?r#`SA+i zQ)uJ_^b9669_mv!%jUZupxYUIH?-!ykd5fubWI>XEO0r%y*>=K^yfmY|De%1PNcCa z5AI5(BJV@*am&QK&xJ0Oz^b^WgMeM{2@U)~>n;CW7!dE=d>?3o=;6&L@j;esD|r*b w3pHCZdle0juo^4&NjwTk2WIrxO8*nW|3M3@bRmjBE&u=k07*qoM6N<$f^3d3;s5{u diff --git a/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png b/iOS/Resources/Assets.xcassets/1password.imageset/onepassword-navbar@3x.png deleted file mode 100644 index 24b64962a76ed7669ad426768d4745145faee70b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2757 zcmV;$3OePxxBL2e z-n-qKnSFP=d-v{-11GtAXP%jP=9%w2^UUv6PK8`YM@LJYQdfA9$Ob2!?sJqnUn$k& zcwWqNoEF4r<}aZfXOg%n&+`U}>vvqYU!_uS&UxPPcsxE;32~93e6Fjj>q6IYwo&=3 zsrW{|(PfwBd2@jEM}YcW%JU8n4-fx!*}Tq@F?3K`TU*K+) zeQ0`m`tdWkl`yjNh6V~+(TOJPg5e!}V|GF13e(Ld(4hT%_ntg?G9E@bVSv)u+ueN+ zg6&42rXqzJ6MNw)HK082Oxkh8RwVdqrn|+Uw?bbJ&pv2ti{XjZBmia)!0aLYY%zKA zrCAXuJv}|wBK)HWzdm09LPyvEK1UGzXeyODhQT~pD7_xn-rn9Di9|NJuDbP4h;=`o%m$FH2LTygVNdAxt^9gOvaA}F-0rl2)uW8Zf^hS z)2Anb(u6@RY#2gFr`M1yUhG6#|GQoBHanL=Yei^lxcjtdGG1xjyc=T-oDKCmSm=K!s6 z2h2AF8JC?NeHNE^eh-(}Dc3jCYSq*ndRkKUjGZ$@sT>jBIqOgtaR zCwCwyGE)tlm|;U|T|?}O(oLSHaqqS1&~`F_eA1>%I0%$Rf9h_f#&8`d(WqJ+suvt& zJ>;d+PvuAuiR_2gVAFFzSzO%NYnkJV!>*lw)r4u#FVc3!?rYgEiGTsX<1NY$BYCrGtD|sjmuglOLzx(Sg77dHOBO+U_*DbwY9Zo zXjxGFEkt|AjMZ)>7k3$<%K7zI?v}P-GFlt!>%WjGHwy|{vOPoezwC#|^53AnLjqHY z#6Eh#e~s$6Zg#!ogCe``BrrCau_aH3ydRQ+=Mh7YDg`DcCQhRSKh=vTd@&Q&4++f& zg@4=h1mc$+Gn=;3FvI!{0Q2jW@(Ck9Q{0^TWpLo@(52MXdO@hxK$u&I8t)l^`AjVU z!&2q<$n~}+o5P0rRT6e(=L#V=>X8gvzcIq$25JQu&#ot3=(1ipQIMldG{rc%1F;vw z#UY2A4-7)RniEq&0$u2+Ra||ltLtM?&UP;VA;ZZrgh41nfa&#S*nYSStV>~;Q_qOw zY;ZXv`GCb`tdw&ZD{oOjS%uRO&yhH1F;*0VkP=~6-okVw40asAxmelr4WgX%m(kS< zAd6M>Xq3gzFHR!=4Oi!BD--13py?8lqQk;ZizN)mJFQwAtlWMfZL&#*#2<<}M&HLG_KCjPZDy}k(-oi@}5l#K4Ar>>&kZn)4y!G_9xJKasnkZgcL zAIomG=qan{pJs7-eYwiVrW%)m7>%Y}{;`j= zyX>I)WpMC8@sAT7=XJecwB;uK)J%`7v>#x$hFNAT7CVQo@;RL+;ny zy3>f@G;}7Jd?G{3f-*inK8ChDmWhxrYTFK?-_&WV-H-Nvn>v{59bSQqjg4hDo3fxt zWzm#}`4}rw66|7V{Gi0IcE^Z%5tTGd0*a7OQ&W5Ox*4Ega7VbJ{r9!0?5P+k=4VpsRv61or&Z^Ay1MQKkk97`o=WY2Hfz&!G({4C-mmCJkJ^dR zpa%fv+8lZf6MDP4Za~v>Zew9f_Hq3+@XS0A6saTK=xb#BlU`rxkJy)I7^W}K>9y+T z!PH04`lpZ^MOktTC?{xm`@&;3L0(WuG0U0qHh?jVkWA{BBEMAY8biZf&pG)q@My`_ z77u{P-F&%^lho? zZxTqk>?gC-i2(&h2#@t<6V zZfa_}3+>nm+qNLqXh`{An`#9&FvER_7LdtLphF;eZvTv$pFcDhkB7Aht$Lg@jkYA@Jzxk~=617C|Ar=Lm=;6Fo)%RY{*&YKxZV*uSsOUub!A8|#U zZIiw>e(OL-2J2tVU&VL~a35y$i|aBi*H2@G>niyBDX*g)PuBGBrvyRk&mI2%OFKc( zEYRd;cT}k>F_;^m^kERiUSxigN$iH)sG3G~ Date: Mon, 24 Jan 2022 17:20:07 -0500 Subject: [PATCH 13/70] Assert that our UIKeyCommand instances should take priority over system behavior. This may be a bit more blunt than is strictly necessary, as I think there is only contention for e.g. unmodified keystrokes including bare arrow keys, but I also don't think that it will probably hurt anything to assert that all of our key commands should be active? --- iOS/KeyboardManager.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/iOS/KeyboardManager.swift b/iOS/KeyboardManager.swift index fabeb7808..981fa5eb4 100644 --- a/iOS/KeyboardManager.swift +++ b/iOS/KeyboardManager.swift @@ -47,7 +47,11 @@ class KeyboardManager { static func createKeyCommand(title: String, action: String, input: String, modifiers: UIKeyModifierFlags) -> UIKeyCommand { let selector = NSSelectorFromString(action) - return UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on) + let keyCommand = UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on) + if #available(iOS 15.0, *) { + keyCommand.wantsPriorityOverSystemBehavior = true + } + return keyCommand } } @@ -62,7 +66,11 @@ private extension KeyboardManager { if let title = keyEntry["title"] as? String { return KeyboardManager.createKeyCommand(title: title, action: action, input: input, modifiers: modifiers) } else { - return UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action)) + let keyCommand = UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action)) + if #available(iOS 15.0, *) { + keyCommand.wantsPriorityOverSystemBehavior = true + } + return keyCommand } } From 8bb2a2928c93ed0cb5650c3dd722540e1a1da769 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Wed, 26 Jan 2022 10:31:18 +0800 Subject: [PATCH 14/70] fix layout in landscape --- iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift index 2c8b9d6a4..84bafbc91 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift @@ -34,6 +34,7 @@ struct MasterFeedTableViewCellLayout { init(cellWidth: CGFloat, insets: UIEdgeInsets, label: UILabel, unreadCountView: MasterFeedUnreadCountView, showingEditingControl: Bool, indent: Bool, shouldShowDisclosure: Bool, itemIsInFolder: Bool) { + print(insets) var initialIndent = insets.left if indent { initialIndent += MasterFeedTableViewCellLayout.indentWidth @@ -44,7 +45,7 @@ struct MasterFeedTableViewCellLayout { var rDisclosure = CGRect.zero if shouldShowDisclosure { rDisclosure.size = MasterFeedTableViewCellLayout.disclosureButtonSize - rDisclosure.origin.x = bounds.origin.x + rDisclosure.origin.x = insets.left } // Favicon @@ -73,7 +74,7 @@ struct MasterFeedTableViewCellLayout { } // Title - var rLabelx = MasterFeedTableViewCellLayout.disclosureButtonSize.width + var rLabelx = MasterFeedTableViewCellLayout.disclosureButtonSize.width + insets.left if itemIsInFolder { rLabelx += MasterFeedTableViewCellLayout.disclosureButtonSize.width - (rFavicon.width / 2) } From 6c88b322dd26989b634e9f9e7712becd6300bed1 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Tue, 25 Jan 2022 20:02:52 -0800 Subject: [PATCH 15/70] Update release notes and version for 6103. --- Technotes/ReleaseNotes-iOS.markdown | 5 +++++ xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Technotes/ReleaseNotes-iOS.markdown b/Technotes/ReleaseNotes-iOS.markdown index a1732684c..d0db5db3a 100644 --- a/Technotes/ReleaseNotes-iOS.markdown +++ b/Technotes/ReleaseNotes-iOS.markdown @@ -1,5 +1,10 @@ # iOS Release Notes +### 6.1 TestFlight build 6103 - 25 Jan 2022 + +Fixed regression with keyboard shortcuts. +Fixed crashing bug adding an account. + ### 6.1 TestFlight build 6102 - 23 Jan 2022 Article themes. Several themes ship with the app, and you can create your own. You can change the theme in Preferences. diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 731951dec..4dbd4ef00 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 6.1 -CURRENT_PROJECT_VERSION = 6102 +CURRENT_PROJECT_VERSION = 6103 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From 9a16be3261e440652e0964b9d10048be50329ac0 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 26 Jan 2022 14:34:55 -0800 Subject: [PATCH 16/70] Remove debug print statement --- iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift index 84bafbc91..d4c7338bb 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift @@ -34,7 +34,6 @@ struct MasterFeedTableViewCellLayout { init(cellWidth: CGFloat, insets: UIEdgeInsets, label: UILabel, unreadCountView: MasterFeedUnreadCountView, showingEditingControl: Bool, indent: Bool, shouldShowDisclosure: Bool, itemIsInFolder: Bool) { - print(insets) var initialIndent = insets.left if indent { initialIndent += MasterFeedTableViewCellLayout.indentWidth From 542405017193f52ababe2cd96326fa94ca847e94 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 26 Jan 2022 15:40:47 -0800 Subject: [PATCH 17/70] Change to new iOS 3 panel UISplitViewController --- NetNewsWire.xcodeproj/project.pbxproj | 4 - iOS/Article/ArticleViewController.swift | 13 + iOS/Base.lproj/Main.storyboard | 24 +- iOS/MasterFeed/MasterFeedViewController.swift | 1 + .../MasterTimelineViewController.swift | 9 +- iOS/RootSplitViewController.swift | 5 - iOS/SceneCoordinator.swift | 323 +++--------------- iOS/SceneDelegate.swift | 17 +- .../InteractiveNavigationController.swift | 72 ---- 9 files changed, 100 insertions(+), 368 deletions(-) delete mode 100644 iOS/UIKit Extensions/InteractiveNavigationController.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 8adb942c4..5ef2b4958 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -271,7 +271,6 @@ 518C3193237B00D9004D740F /* DetailIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5141E7552374A2890013FF27 /* DetailIconSchemeHandler.swift */; }; 518C3194237B00DA004D740F /* DetailIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5141E7552374A2890013FF27 /* DetailIconSchemeHandler.swift */; }; 518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */; }; - 51934CCB230F599B006127BE /* InteractiveNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */; }; 51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; }; 51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; 51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; @@ -1268,7 +1267,6 @@ 518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = ""; }; 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController-Extensions.swift"; sourceTree = ""; }; - 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveNavigationController.swift; sourceTree = ""; }; 51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = ""; }; 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = ""; }; 5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RedditFeedProvider-Extensions.swift"; sourceTree = ""; }; @@ -2027,7 +2025,6 @@ 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */, 512AF9C1236ED52C0066F8BE /* ImageHeaderView.swift */, 512AF9DC236F05230066F8BE /* InteractiveLabel.swift */, - 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */, 51A9A5F42380F6A60033AADF /* ModalNavigationController.swift */, 51EAED95231363EF00A9EEE3 /* NonIntrinsicButton.swift */, 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */, @@ -4201,7 +4198,6 @@ 51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */, 51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */, 51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */, - 51934CCB230F599B006127BE /* InteractiveNavigationController.swift in Sources */, 769F2ED513DA03EE75B993A8 /* NewsBlurAccountViewController.swift in Sources */, 51BC4B01247277E0000A6ED8 /* URL-Extensions.swift in Sources */, ); diff --git a/iOS/Article/ArticleViewController.swift b/iOS/Article/ArticleViewController.swift index 81e537dc3..144c1b7a2 100644 --- a/iOS/Article/ArticleViewController.swift +++ b/iOS/Article/ArticleViewController.swift @@ -45,6 +45,8 @@ class ArticleViewController: UIViewController { weak var coordinator: SceneCoordinator! + private let poppableDelegate = PoppableGestureRecognizerDelegate() + var article: Article? { didSet { if let controller = currentWebViewController, controller.article != article { @@ -101,6 +103,11 @@ class ArticleViewController: UIViewController { articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside) toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6) + if let parentNavController = navigationController?.parent as? UINavigationController { + poppableDelegate.navigationController = parentNavController + parentNavController.interactivePopGestureRecognizer?.delegate = poppableDelegate + } + pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:]) pageViewController.delegate = self pageViewController.dataSource = self @@ -154,6 +161,11 @@ class ArticleViewController: UIViewController { updateUI() } + + override func viewWillAppear(_ animated: Bool) { + navigationController?.isToolbarHidden = false + super.viewWillAppear(animated) + } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) @@ -161,6 +173,7 @@ class ArticleViewController: UIViewController { } override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) if searchBar != nil && !searchBar.isHidden { endFind() } diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index 4ccb0e39a..148758d6f 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -17,7 +17,7 @@ -