mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Changes
- Removes unused classes - Posts a notification when the app is opened via external context which allows the SwiftUI SettingsView to dismiss itself.
This commit is contained in:
@@ -235,12 +235,9 @@
|
||||
515A5181243E90260089E588 /* ExtensionPointIdentifer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5176243E90200089E588 /* ExtensionPointIdentifer.swift */; };
|
||||
515D4FCA23257CB500EE1167 /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; };
|
||||
515D4FCC2325815A00EE1167 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; };
|
||||
516244E3241E19F000B61C47 /* ColorPaletteTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */; };
|
||||
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */; };
|
||||
51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; };
|
||||
51627A93238A3836007B3B4B /* CroppingPreviewParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */; };
|
||||
516A093723609A3600EAE89B /* SettingsComboTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */; };
|
||||
516A09392360A2AE00EAE89B /* SettingsComboTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */; };
|
||||
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; };
|
||||
516A09402361240900EAE89B /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A093F2361240900EAE89B /* Account.storyboard */; };
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A09412361248000EAE89B /* Inspector.storyboard */; };
|
||||
@@ -298,7 +295,6 @@
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; };
|
||||
51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */; };
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16992235E10D600EB091F /* AddAccountViewController.swift */; };
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16993235E10D600EB091F /* SettingsViewController.swift */; };
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */; };
|
||||
51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; };
|
||||
51A737AE24DB19730015FA66 /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 51A737AD24DB19730015FA66 /* RSCore */; };
|
||||
@@ -853,6 +849,8 @@
|
||||
DDF9E1D728EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
|
||||
DDF9E1D828EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
|
||||
DDF9E1D928EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */; };
|
||||
DF32ABE829493193008E3A12 /* SettingsComboTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */; };
|
||||
DF32ABE929493193008E3A12 /* SettingsComboTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */; };
|
||||
DF3630EB2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; };
|
||||
DF3630EC2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; };
|
||||
DF3630ED2936183D00326FB8 /* OPMLDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3630EA2936183D00326FB8 /* OPMLDocument.swift */; };
|
||||
@@ -880,8 +878,6 @@
|
||||
DFD406FC291FB63B00C02962 /* SettingsHelpSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */; };
|
||||
DFD406FF291FDC0C00C02962 /* DisplayAndBehaviorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFD406FE291FDC0C00C02962 /* DisplayAndBehaviorsView.swift */; };
|
||||
DFFB8FC2279B75E300AC21D7 /* Account in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 51BC2F4A24D343A500E90810 /* Account */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
DFFC199827A0D0D7004B7AEF /* NotificationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */; };
|
||||
DFFC199A27A0D32A004B7AEF /* NotificationsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC199927A0D32A004B7AEF /* NotificationsTableViewCell.swift */; };
|
||||
DFFC4E7428E95C01006B82AF /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFFC4E7328E95C01006B82AF /* AboutView.swift */; };
|
||||
FF3ABF13232599810074C542 /* ArticleSorterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF09232599450074C542 /* ArticleSorterTests.swift */; };
|
||||
FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
|
||||
@@ -1268,12 +1264,9 @@
|
||||
515D4FCB2325815A00EE1167 /* SafariExt.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SafariExt.js; sourceTree = "<group>"; };
|
||||
515D4FCD2325909200EE1167 /* NetNewsWire_iOS_ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = "<group>"; };
|
||||
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteTableViewController.swift; sourceTree = "<group>"; };
|
||||
51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drag.swift"; sourceTree = "<group>"; };
|
||||
51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = "<group>"; };
|
||||
51627A92238A3836007B3B4B /* CroppingPreviewParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CroppingPreviewParameters.swift; sourceTree = "<group>"; };
|
||||
516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsComboTableViewCell.xib; sourceTree = "<group>"; };
|
||||
516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsComboTableViewCell.swift; sourceTree = "<group>"; };
|
||||
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
|
||||
516A093F2361240900EAE89B /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = "<group>"; };
|
||||
516A09412361248000EAE89B /* Inspector.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Inspector.storyboard; sourceTree = "<group>"; };
|
||||
@@ -1318,7 +1311,6 @@
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
||||
51A16991235E10D600EB091F /* AccountInspectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInspectorViewController.swift; sourceTree = "<group>"; };
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = "<group>"; };
|
||||
51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerAccountCell.xib; sourceTree = "<group>"; };
|
||||
@@ -1616,6 +1608,8 @@
|
||||
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = "<group>"; };
|
||||
DDF9E1D628EDF2FC000BC355 /* notificationSoundBlip.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = notificationSoundBlip.mp3; sourceTree = "<group>"; };
|
||||
DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsComboTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsComboTableViewCell.xib; sourceTree = "<group>"; };
|
||||
DF3630EA2936183D00326FB8 /* OPMLDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLDocument.swift; sourceTree = "<group>"; };
|
||||
DF3630EE293618A900326FB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
DF394EFF29357A180081EB6E /* NewArticleNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArticleNotificationsView.swift; sourceTree = "<group>"; };
|
||||
@@ -1636,8 +1630,6 @@
|
||||
DFD406FB291FB63B00C02962 /* SettingsHelpSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsHelpSheets.swift; sourceTree = "<group>"; };
|
||||
DFD406FE291FDC0C00C02962 /* DisplayAndBehaviorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayAndBehaviorsView.swift; sourceTree = "<group>"; };
|
||||
DFD6AACB27ADE80900463FAD /* NewsFax.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = NewsFax.nnwtheme; sourceTree = "<group>"; };
|
||||
DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsViewController.swift; sourceTree = "<group>"; };
|
||||
DFFC199927A0D32A004B7AEF /* NotificationsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DFFC4E7328E95C01006B82AF /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = "<group>"; };
|
||||
FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorter.swift; sourceTree = "<group>"; };
|
||||
@@ -2031,17 +2023,13 @@
|
||||
519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */,
|
||||
5137C2E926F63AE6009EFEDB /* ArticleThemeImporter.swift */,
|
||||
510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */,
|
||||
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */,
|
||||
519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */,
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */,
|
||||
516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */,
|
||||
516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */,
|
||||
DF32ABE629493192008E3A12 /* SettingsComboTableViewCell.swift */,
|
||||
DF32ABE729493193008E3A12 /* SettingsComboTableViewCell.xib */,
|
||||
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */,
|
||||
5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */,
|
||||
5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */,
|
||||
DFFC199727A0D0D7004B7AEF /* NotificationsViewController.swift */,
|
||||
DFFC199927A0D32A004B7AEF /* NotificationsTableViewCell.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
@@ -3545,7 +3533,6 @@
|
||||
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
|
||||
51BB7C312335ACDE008E8144 /* page.html in Resources */,
|
||||
512392C324E3451400F11704 /* TwitterAdd.storyboard in Resources */,
|
||||
516A093723609A3600EAE89B /* SettingsComboTableViewCell.xib in Resources */,
|
||||
51077C5A27A86D16000C71DB /* Hyperlegible.nnwtheme in Resources */,
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */,
|
||||
DDF9E1D928EDF2FC000BC355 /* notificationSoundBlip.mp3 in Resources */,
|
||||
@@ -3570,6 +3557,7 @@
|
||||
DFCE4F9528EF278300405869 /* Thanks.md in Resources */,
|
||||
51DEE81426FB9233006DAA56 /* Appanoose.nnwtheme in Resources */,
|
||||
51E36E8C239D6765006F47A5 /* AddFeedSelectFolderTableViewCell.xib in Resources */,
|
||||
DF32ABE929493193008E3A12 /* SettingsComboTableViewCell.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -4139,12 +4127,12 @@
|
||||
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
|
||||
B2B80778239C4C7000F191E0 /* RSImage-AppIcons.swift in Sources */,
|
||||
518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */,
|
||||
DFFC199827A0D0D7004B7AEF /* NotificationsViewController.swift in Sources */,
|
||||
DFD406FF291FDC0C00C02962 /* DisplayAndBehaviorsView.swift in Sources */,
|
||||
51A9A5F52380F6A60033AADF /* ModalNavigationController.swift in Sources */,
|
||||
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
|
||||
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
|
||||
5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */,
|
||||
DF32ABE829493193008E3A12 /* SettingsComboTableViewCell.swift in Sources */,
|
||||
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
|
||||
51B62E68233186730085F949 /* IconView.swift in Sources */,
|
||||
179D280D26F73D83003B2E0A /* ArticleThemePlist.swift in Sources */,
|
||||
@@ -4152,7 +4140,6 @@
|
||||
51C45291226509C800C03939 /* SmartFeed.swift in Sources */,
|
||||
51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */,
|
||||
511B9807237DCAC90028BCAA /* UserInfoKey.swift in Sources */,
|
||||
DFFC199A27A0D32A004B7AEF /* NotificationsTableViewCell.swift in Sources */,
|
||||
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
|
||||
51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */,
|
||||
517630232336657E00E15FFF /* WebViewProvider.swift in Sources */,
|
||||
@@ -4167,7 +4154,6 @@
|
||||
51AB8AB323B7F4C6008F147D /* WebViewController.swift in Sources */,
|
||||
DFD406F7291FB1A600C02962 /* SafariView.swift in Sources */,
|
||||
DF3630ED2936183D00326FB8 /* OPMLDocument.swift in Sources */,
|
||||
516A09392360A2AE00EAE89B /* SettingsComboTableViewCell.swift in Sources */,
|
||||
176813D22564BA5900D98635 /* WidgetDataEncoder.swift in Sources */,
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */,
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */,
|
||||
@@ -4218,7 +4204,6 @@
|
||||
5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */,
|
||||
51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */,
|
||||
5142192A23522B5500E07E2C /* ImageViewController.swift in Sources */,
|
||||
516244E3241E19F000B61C47 /* ColorPaletteTableViewController.swift in Sources */,
|
||||
51C45258226508CF00C03939 /* AppAssets.swift in Sources */,
|
||||
51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */,
|
||||
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */,
|
||||
@@ -4317,7 +4302,6 @@
|
||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||
510FFAB326EEA22C00F32265 /* ArticleThemesTableViewController.swift in Sources */,
|
||||
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */,
|
||||
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
||||
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
|
||||
769F2ED513DA03EE75B993A8 /* NewsBlurAccountViewController.swift in Sources */,
|
||||
|
||||
@@ -12,6 +12,7 @@ import Articles
|
||||
extension Notification.Name {
|
||||
static let InspectableObjectsDidChange = Notification.Name("TimelineSelectionDidChangeNotification")
|
||||
static let UserDidAddFeed = Notification.Name("UserDidAddFeedNotification")
|
||||
static let LaunchedFromExternalAction = Notification.Name("LaunchedFromExternalAction")
|
||||
|
||||
#if !MAC_APP_STORE
|
||||
static let WebInspectorEnabledDidChange = Notification.Name("WebInspectorEnabledDidChange")
|
||||
|
||||
@@ -1326,8 +1326,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
if presentedController.isKind(of: SFSafariViewController.self) {
|
||||
presentedController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
guard let settings = presentedController.children.first as? SettingsViewController else { return }
|
||||
settings.dismiss(animated: true, completion: nil)
|
||||
|
||||
// There's no obvious way to detect if the presented controller
|
||||
// is the SwiftUI UIHostingController<SettingsView>. Posting a notification
|
||||
// which it can react to seems to be the simplest solution.
|
||||
NotificationCenter.default.post(name: .LaunchedFromExternalAction, object: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// ColorPaletteTableViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 3/15/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ColorPaletteTableViewController: UITableViewController {
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return UserInterfaceColorPalette.allCases.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
let rowColorPalette = UserInterfaceColorPalette.allCases[indexPath.row]
|
||||
cell.textLabel?.text = String(describing: rowColorPalette)
|
||||
if rowColorPalette == AppDefaults.userInterfaceColorPalette {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
if let colorPalette = UserInterfaceColorPalette(rawValue: indexPath.row) {
|
||||
AppDefaults.userInterfaceColorPalette = colorPalette
|
||||
}
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//
|
||||
// NotificationsTableViewCell.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 26/01/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import UserNotifications
|
||||
|
||||
|
||||
class NotificationsTableViewCell: VibrantBasicTableViewCell {
|
||||
|
||||
@IBOutlet weak var notificationsSwitch: UISwitch!
|
||||
@IBOutlet weak var notificationsLabel: UILabel!
|
||||
@IBOutlet weak var notificationsImageView: UIImageView!
|
||||
weak var feed: WebFeed?
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
func configure(_ webFeed: WebFeed) {
|
||||
self.feed = webFeed
|
||||
var isOn = false
|
||||
if webFeed.isNotifyAboutNewArticles == nil {
|
||||
isOn = false
|
||||
} else {
|
||||
isOn = webFeed.isNotifyAboutNewArticles!
|
||||
}
|
||||
notificationsSwitch.isOn = isOn
|
||||
notificationsSwitch.addTarget(self, action: #selector(toggleWebFeedNotification(_:)), for: .touchUpInside)
|
||||
notificationsLabel.text = webFeed.nameForDisplay
|
||||
notificationsImageView.image = IconImageCache.shared.imageFor(webFeed.feedID!)?.image
|
||||
notificationsImageView.layer.cornerRadius = 4
|
||||
}
|
||||
|
||||
@objc
|
||||
private func toggleWebFeedNotification(_ sender: Any) {
|
||||
guard let feed = feed else {
|
||||
return
|
||||
}
|
||||
if feed.isNotifyAboutNewArticles == nil {
|
||||
feed.isNotifyAboutNewArticles = true
|
||||
}
|
||||
else {
|
||||
feed.isNotifyAboutNewArticles!.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
//
|
||||
// NotificationsViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 26/01/2022.
|
||||
// Copyright © 2022 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import Account
|
||||
import UserNotifications
|
||||
|
||||
struct NotificationsViewControllerRepresentable: UIViewControllerRepresentable {
|
||||
func makeUIViewController(context: Context) -> NotificationsViewController {
|
||||
let storyboard = UIStoryboard(name: "Settings", bundle: .main)
|
||||
let controller = storyboard.instantiateViewController(withIdentifier: "NotificationsViewController") as! NotificationsViewController
|
||||
|
||||
context.coordinator.parentObserver = controller.observe(\.parent, changeHandler: { vc, _ in
|
||||
vc.parent?.title = vc.title
|
||||
vc.parent?.navigationItem.rightBarButtonItems = vc.navigationItem.rightBarButtonItems
|
||||
})
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: NotificationsViewController, context: Context) {
|
||||
//
|
||||
}
|
||||
|
||||
typealias UIViewControllerType = NotificationsViewController
|
||||
|
||||
class Coordinator {
|
||||
var parentObserver: NSKeyValueObservation?
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Self.Coordinator { Coordinator() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
class NotificationsViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var notificationsTableView: UITableView!
|
||||
|
||||
private lazy var searchController: UISearchController = {
|
||||
let searchController = UISearchController(searchResultsController: nil)
|
||||
searchController.searchBar.placeholder = NSLocalizedString("Find a feed", comment: "Find a feed")
|
||||
searchController.searchBar.searchBarStyle = .minimal
|
||||
searchController.delegate = self
|
||||
searchController.searchBar.delegate = self
|
||||
searchController.searchBar.sizeToFit()
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
self.definesPresentationContext = true
|
||||
return searchController
|
||||
}()
|
||||
private var status: UNAuthorizationStatus = .notDetermined
|
||||
private var newArticleNotificationFilter: Bool = false {
|
||||
didSet {
|
||||
filterButton.menu = notificationFilterMenu()
|
||||
}
|
||||
}
|
||||
private var filterButton: UIBarButtonItem!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = NSLocalizedString("New Article Notifications", comment: "Notifications")
|
||||
|
||||
navigationItem.searchController = searchController
|
||||
notificationsTableView.isPrefetchingEnabled = false
|
||||
|
||||
filterButton = UIBarButtonItem(
|
||||
title: nil,
|
||||
image: AppAssets.moreImage,
|
||||
primaryAction: nil,
|
||||
menu: notificationFilterMenu())
|
||||
|
||||
navigationItem.rightBarButtonItem = filterButton
|
||||
|
||||
reloadNotificationTableView()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateCellsFrom(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadVisibleCells(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadNotificationTableView(_:)), name: UIScene.willEnterForegroundNotification, object: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
private func reloadNotificationTableView(_ sender: Any? = nil) {
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
DispatchQueue.main.async {
|
||||
self.status = settings.authorizationStatus
|
||||
if self.status != .authorized {
|
||||
self.filterButton.isEnabled = false
|
||||
self.newArticleNotificationFilter = false
|
||||
}
|
||||
self.notificationsTableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
private func updateCellsFrom(_ notification: Notification) {
|
||||
guard let webFeed = notification.userInfo?[UserInfoKey.webFeed] as? WebFeed else { return }
|
||||
if let visibleIndexPaths = notificationsTableView.indexPathsForVisibleRows {
|
||||
for path in visibleIndexPaths {
|
||||
if let cell = notificationsTableView.cellForRow(at: path) as? NotificationsTableViewCell {
|
||||
if cell.feed! == webFeed {
|
||||
cell.configure(webFeed)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
private func reloadVisibleCells(_ notification: Notification) {
|
||||
guard let faviconURLString = notification.userInfo?["faviconURL"] as? String,
|
||||
let faviconHost = URL(string: faviconURLString)?.host else {
|
||||
return
|
||||
}
|
||||
|
||||
for cell in notificationsTableView.visibleCells {
|
||||
if let notificationCell = cell as? NotificationsTableViewCell {
|
||||
if let feedURLHost = URL(string: notificationCell.feed!.url)?.host {
|
||||
if faviconHost == feedURLHost {
|
||||
notificationCell.configure(notificationCell.feed!)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func notificationFilterMenu() -> UIMenu {
|
||||
|
||||
if filterButton != nil {
|
||||
if newArticleNotificationFilter {
|
||||
filterButton.image = AppAssets.moreImageFill
|
||||
} else {
|
||||
filterButton.image = AppAssets.moreImage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let filterMenu = UIMenu(title: "",
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: NSLocalizedString("Show Feeds with Notifications Enabled", comment: "Feeds with Notifications"),
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: newArticleNotificationFilter ? .on : .off,
|
||||
handler: { [weak self] _ in
|
||||
self?.newArticleNotificationFilter.toggle()
|
||||
self?.notificationsTableView.reloadData()
|
||||
})])
|
||||
|
||||
|
||||
var menus = [UIMenuElement]()
|
||||
menus.append(filterMenu)
|
||||
|
||||
for account in AccountManager.shared.sortedActiveAccounts {
|
||||
let accountMenu = UIMenu(title: account.nameForDisplay, image: nil, identifier: nil, options: .singleSelection, children: [enableAllAction(for: account), disableAllAction(for: account)])
|
||||
menus.append(accountMenu)
|
||||
}
|
||||
|
||||
let combinedMenu = UIMenu(title: "",
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
options: .displayInline,
|
||||
children: menus)
|
||||
|
||||
return combinedMenu
|
||||
}
|
||||
|
||||
private func enableAllAction(for account: Account) -> UIAction {
|
||||
let action = UIAction(title: NSLocalizedString("Enable All Notifications", comment: "Enable All"),
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off) { [weak self] _ in
|
||||
for feed in account.flattenedWebFeeds() {
|
||||
feed.isNotifyAboutNewArticles = true
|
||||
}
|
||||
self?.notificationsTableView.reloadData()
|
||||
self?.filterButton.menu = self?.notificationFilterMenu()
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
private func disableAllAction(for account: Account) -> UIAction {
|
||||
let action = UIAction(title: NSLocalizedString("Disable All Notifications", comment: "Disable All"),
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off) { [weak self] _ in
|
||||
for feed in account.flattenedWebFeeds() {
|
||||
feed.isNotifyAboutNewArticles = false
|
||||
}
|
||||
self?.notificationsTableView.reloadData()
|
||||
self?.filterButton.menu = self?.notificationFilterMenu()
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
// MARK: - Feed Filtering
|
||||
|
||||
private func sortedWebFeedsForAccount(_ account: Account) -> [WebFeed] {
|
||||
return Array(account.flattenedWebFeeds()).sorted(by: { $0.nameForDisplay.caseInsensitiveCompare($1.nameForDisplay) == .orderedAscending })
|
||||
}
|
||||
|
||||
private func filteredWebFeeds(_ searchText: String? = "", account: Account) -> [WebFeed] {
|
||||
sortedWebFeedsForAccount(account).filter { feed in
|
||||
return feed.nameForDisplay.lowercased().contains(searchText!.lowercased())
|
||||
}
|
||||
}
|
||||
|
||||
private func feedsWithNotificationsEnabled(_ account: Account) -> [WebFeed] {
|
||||
sortedWebFeedsForAccount(account).filter { feed in
|
||||
return feed.isNotifyAboutNewArticles == true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension NotificationsViewController: UITableViewDataSource {
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
if status == .denied { return 1 }
|
||||
return 1 + AccountManager.shared.activeAccounts.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if section == 0 {
|
||||
if status == .denied { return 1 }
|
||||
return 0
|
||||
}
|
||||
if searchController.isActive {
|
||||
return filteredWebFeeds(searchController.searchBar.text, account: AccountManager.shared.sortedActiveAccounts[section - 1]).count
|
||||
} else if newArticleNotificationFilter == true {
|
||||
return feedsWithNotificationsEnabled(AccountManager.shared.sortedActiveAccounts[section - 1]).count
|
||||
} else {
|
||||
return AccountManager.shared.sortedActiveAccounts[section - 1].flattenedWebFeeds().count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if indexPath.section == 0 {
|
||||
let openSettingsCell = tableView.dequeueReusableCell(withIdentifier: "OpenSettingsCell") as! VibrantBasicTableViewCell
|
||||
return openSettingsCell
|
||||
} else {
|
||||
if searchController.isActive {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "NotificationsCell") as! NotificationsTableViewCell
|
||||
let account = AccountManager.shared.sortedActiveAccounts[indexPath.section - 1]
|
||||
cell.configure(filteredWebFeeds(searchController.searchBar.text, account: account)[indexPath.row])
|
||||
return cell
|
||||
} else if newArticleNotificationFilter == true {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "NotificationsCell") as! NotificationsTableViewCell
|
||||
let account = AccountManager.shared.sortedActiveAccounts[indexPath.section - 1]
|
||||
cell.configure(feedsWithNotificationsEnabled(account)[indexPath.row])
|
||||
return cell
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "NotificationsCell") as! NotificationsTableViewCell
|
||||
let account = AccountManager.shared.sortedActiveAccounts[indexPath.section - 1]
|
||||
cell.configure(sortedWebFeedsForAccount(account)[indexPath.row])
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
if section == 0 { return nil }
|
||||
return AccountManager.shared.sortedActiveAccounts[section - 1].nameForDisplay
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
if section == 0 {
|
||||
if status == .denied {
|
||||
return NSLocalizedString("Notification permissions are currently denied. Enable notifications in the Settings app.", comment: "Notifications denied.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension NotificationsViewController: UITableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
if indexPath.section == 0 {
|
||||
UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UISearchControllerDelegate
|
||||
extension NotificationsViewController: UISearchControllerDelegate {
|
||||
|
||||
func didDismissSearchController(_ searchController: UISearchController) {
|
||||
print(#function)
|
||||
searchController.isActive = false
|
||||
notificationsTableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UISearchBarDelegate
|
||||
extension NotificationsViewController: UISearchBarDelegate {
|
||||
|
||||
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
||||
searchController.isActive = true
|
||||
newArticleNotificationFilter = false
|
||||
notificationsTableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
notificationsTableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,515 +0,0 @@
|
||||
//
|
||||
// SettingsViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/24/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import CoreServices
|
||||
import SafariServices
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
import UserNotifications
|
||||
import RSCore
|
||||
|
||||
class SettingsViewController: UITableViewController, Logging {
|
||||
|
||||
private weak var opmlAccount: Account?
|
||||
|
||||
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
|
||||
@IBOutlet weak var groupByFeedSwitch: UISwitch!
|
||||
@IBOutlet weak var refreshClearsReadArticlesSwitch: UISwitch!
|
||||
@IBOutlet weak var articleThemeDetailLabel: UILabel!
|
||||
@IBOutlet weak var confirmMarkAllAsReadSwitch: UISwitch!
|
||||
@IBOutlet weak var showFullscreenArticlesSwitch: UISwitch!
|
||||
@IBOutlet weak var colorPaletteDetailLabel: UILabel!
|
||||
@IBOutlet weak var openLinksInNetNewsWire: UISwitch!
|
||||
|
||||
var scrollToArticlesSection = false
|
||||
weak var presentingParentController: UIViewController?
|
||||
|
||||
var notificationStatus: UNAuthorizationStatus = .notDetermined
|
||||
|
||||
override func viewDidLoad() {
|
||||
// This hack mostly works around a bug in static tables with dynamic type. See: https://spin.atomicobject.com/2018/10/15/dynamic-type-static-uitableview/
|
||||
NotificationCenter.default.removeObserver(tableView!, name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(activeExtensionPointsDidChange), name: .ActiveExtensionPointsDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(refreshNotificationStatus(_:)), name: UIScene.willEnterForegroundNotification, object: nil)
|
||||
|
||||
|
||||
tableView.register(UINib(nibName: "SettingsComboTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsComboTableViewCell")
|
||||
tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell")
|
||||
|
||||
refreshNotificationStatus()
|
||||
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 44
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if AppDefaults.shared.timelineSortDirection == .orderedAscending {
|
||||
timelineSortOrderSwitch.isOn = true
|
||||
} else {
|
||||
timelineSortOrderSwitch.isOn = false
|
||||
}
|
||||
|
||||
if AppDefaults.shared.timelineGroupByFeed {
|
||||
groupByFeedSwitch.isOn = true
|
||||
} else {
|
||||
groupByFeedSwitch.isOn = false
|
||||
}
|
||||
|
||||
if AppDefaults.shared.refreshClearsReadArticles {
|
||||
refreshClearsReadArticlesSwitch.isOn = true
|
||||
} else {
|
||||
refreshClearsReadArticlesSwitch.isOn = false
|
||||
}
|
||||
|
||||
articleThemeDetailLabel.text = ArticleThemesManager.shared.currentTheme.name
|
||||
|
||||
if AppDefaults.shared.confirmMarkAllAsRead {
|
||||
confirmMarkAllAsReadSwitch.isOn = true
|
||||
} else {
|
||||
confirmMarkAllAsReadSwitch.isOn = false
|
||||
}
|
||||
|
||||
colorPaletteDetailLabel.text = String(describing: AppDefaults.userInterfaceColorPalette)
|
||||
openLinksInNetNewsWire.isOn = !AppDefaults.shared.useSystemBrowser
|
||||
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
self.tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
|
||||
if scrollToArticlesSection {
|
||||
tableView.scrollToRow(at: IndexPath(row: 0, section: 4), at: .top, animated: true)
|
||||
scrollToArticlesSection = false
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func refreshNotificationStatus(_ sender: Any? = nil) {
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
DispatchQueue.main.async {
|
||||
self.notificationStatus = settings.authorizationStatus
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UITableView
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
switch section {
|
||||
case 0:
|
||||
if notificationStatus == .authorized { return 2 }
|
||||
return 1
|
||||
case 1:
|
||||
return AccountManager.shared.accounts.count + 1
|
||||
case 2:
|
||||
return ExtensionPointManager.shared.activeExtensionPoints.count + 1
|
||||
case 3:
|
||||
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
|
||||
if AccountManager.shared.activeAccounts.isEmpty || AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() {
|
||||
return defaultNumberOfRows - 1
|
||||
}
|
||||
return defaultNumberOfRows
|
||||
case 5:
|
||||
return 3
|
||||
default:
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell: UITableViewCell
|
||||
switch indexPath.section {
|
||||
case 1:
|
||||
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath)
|
||||
cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts")
|
||||
} else {
|
||||
let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsComboTableViewCell", for: indexPath) as! SettingsComboTableViewCell
|
||||
acctCell.applyThemeProperties()
|
||||
let account = sortedAccounts[indexPath.row]
|
||||
acctCell.comboImage?.image = AppAssets.image(for: account.type)
|
||||
acctCell.comboNameLabel?.text = account.nameForDisplay
|
||||
cell = acctCell
|
||||
}
|
||||
|
||||
case 2:
|
||||
|
||||
let extensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values)
|
||||
if indexPath.row == extensionPoints.count {
|
||||
cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath)
|
||||
cell.textLabel?.text = NSLocalizedString("Add Extension", comment: "Extensions")
|
||||
} else {
|
||||
let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsComboTableViewCell", for: indexPath) as! SettingsComboTableViewCell
|
||||
acctCell.applyThemeProperties()
|
||||
let extensionPoint = extensionPoints[indexPath.row]
|
||||
acctCell.comboImage?.image = extensionPoint.image
|
||||
acctCell.comboNameLabel?.text = extensionPoint.title
|
||||
cell = acctCell
|
||||
}
|
||||
default:
|
||||
cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
if indexPath.row == 0 {
|
||||
UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!)
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
} else {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: NotificationsViewController.self)
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
case 1:
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: AddAccountViewController.self)
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
} else {
|
||||
let controller = UIStoryboard.inspector.instantiateController(ofType: AccountInspectorViewController.self)
|
||||
controller.account = sortedAccounts[indexPath.row]
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
case 2:
|
||||
let extensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values)
|
||||
if indexPath.row == extensionPoints.count {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: AddExtensionPointViewController.self)
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
} else {
|
||||
let controller = UIStoryboard.inspector.instantiateController(ofType: ExtensionPointInspectorViewController.self)
|
||||
controller.extensionPoint = extensionPoints[indexPath.row]
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
case 3:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
if let sourceView = tableView.cellForRow(at: indexPath) {
|
||||
let sourceRect = tableView.rectForRow(at: indexPath)
|
||||
importOPML(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
case 1:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
if let sourceView = tableView.cellForRow(at: indexPath) {
|
||||
let sourceRect = tableView.rectForRow(at: indexPath)
|
||||
exportOPML(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
case 2:
|
||||
addFeed()
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 4:
|
||||
switch indexPath.row {
|
||||
case 3:
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineCustomizerViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 5:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let articleThemes = UIStoryboard.settings.instantiateController(ofType: ArticleThemesTableViewController.self)
|
||||
self.navigationController?.pushViewController(articleThemes, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 6:
|
||||
let colorPalette = UIStoryboard.settings.instantiateController(ofType: ColorPaletteTableViewController.self)
|
||||
self.navigationController?.pushViewController(colorPalette, animated: true)
|
||||
case 7:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
openURL("https://netnewswire.com/help/ios/6.1/en/")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 1:
|
||||
openURL("https://netnewswire.com/")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 2:
|
||||
openURL(URL.releaseNotes.absoluteString)
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 3:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/blob/main/Technotes/HowToSupportNetNewsWire.markdown")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 4:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 5:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/issues")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 6:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/tree/main/Technotes")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 7:
|
||||
openURL("https://netnewswire.com/slack")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 8:
|
||||
let hosting = UIHostingController(rootView: AboutView())
|
||||
self.navigationController?.pushViewController(hosting, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
return .none
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, indentationLevelForRowAt indexPath: IndexPath) -> Int {
|
||||
return super.tableView(tableView, indentationLevelForRowAt: IndexPath(row: 0, section: 1))
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func done(_ sender: Any) {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func switchTimelineOrder(_ sender: Any) {
|
||||
if timelineSortOrderSwitch.isOn {
|
||||
AppDefaults.shared.timelineSortDirection = .orderedAscending
|
||||
} else {
|
||||
AppDefaults.shared.timelineSortDirection = .orderedDescending
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchGroupByFeed(_ sender: Any) {
|
||||
if groupByFeedSwitch.isOn {
|
||||
AppDefaults.shared.timelineGroupByFeed = true
|
||||
} else {
|
||||
AppDefaults.shared.timelineGroupByFeed = false
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchClearsReadArticles(_ sender: Any) {
|
||||
if refreshClearsReadArticlesSwitch.isOn {
|
||||
AppDefaults.shared.refreshClearsReadArticles = true
|
||||
} else {
|
||||
AppDefaults.shared.refreshClearsReadArticles = false
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchConfirmMarkAllAsRead(_ sender: Any) {
|
||||
if confirmMarkAllAsReadSwitch.isOn {
|
||||
AppDefaults.shared.confirmMarkAllAsRead = true
|
||||
} else {
|
||||
AppDefaults.shared.confirmMarkAllAsRead = false
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchBrowserPreference(_ sender: Any) {
|
||||
if openLinksInNetNewsWire.isOn {
|
||||
AppDefaults.shared.useSystemBrowser = false
|
||||
} else {
|
||||
AppDefaults.shared.useSystemBrowser = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func contentSizeCategoryDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func accountsDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func displayNameDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func activeExtensionPointsDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func browserPreferenceDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: OPML Document Picker
|
||||
|
||||
extension SettingsViewController: UIDocumentPickerDelegate {
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
for url in urls {
|
||||
opmlAccount?.importOPML(url) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure:
|
||||
let title = NSLocalizedString("Import Failed", comment: "Import Failed")
|
||||
let message = NSLocalizedString("We were unable to process the selected file. Please ensure that it is a properly formatted OPML file.", comment: "Import Failed Message")
|
||||
self.presentError(title: title, message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension SettingsViewController {
|
||||
|
||||
func addFeed() {
|
||||
self.dismiss(animated: true)
|
||||
|
||||
let addNavViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddWebFeedViewControllerNav") as! UINavigationController
|
||||
let addViewController = addNavViewController.topViewController as! AddFeedViewController
|
||||
addViewController.initialFeed = AccountManager.netNewsWireNewsURL
|
||||
addViewController.initialFeedName = NSLocalizedString("NetNewsWire News", comment: "NetNewsWire News")
|
||||
addNavViewController.modalPresentationStyle = .formSheet
|
||||
addNavViewController.preferredContentSize = AddFeedViewController.preferredContentSizeForFormSheetDisplay
|
||||
|
||||
presentingParentController?.present(addNavViewController, animated: true)
|
||||
}
|
||||
|
||||
func importOPML(sourceView: UIView, sourceRect: CGRect) {
|
||||
switch AccountManager.shared.activeAccounts.count {
|
||||
case 0:
|
||||
presentError(title: "Error", message: NSLocalizedString("You must have at least one active account.", comment: "Missing active account"))
|
||||
case 1:
|
||||
opmlAccount = AccountManager.shared.activeAccounts.first
|
||||
importOPMLDocumentPicker()
|
||||
default:
|
||||
importOPMLAccountPicker(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
}
|
||||
|
||||
func importOPMLAccountPicker(sourceView: UIView, sourceRect: CGRect) {
|
||||
let title = NSLocalizedString("Choose an account to receive the imported feeds and folders", comment: "Import Account")
|
||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
if let popoverController = alert.popoverPresentationController {
|
||||
popoverController.sourceView = view
|
||||
popoverController.sourceRect = sourceRect
|
||||
}
|
||||
|
||||
for account in AccountManager.shared.sortedActiveAccounts {
|
||||
let action = UIAlertAction(title: account.nameForDisplay, style: .default) { [weak self] action in
|
||||
self?.opmlAccount = account
|
||||
self?.importOPMLDocumentPicker()
|
||||
}
|
||||
alert.addAction(action)
|
||||
}
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alert.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
|
||||
func importOPMLDocumentPicker() {
|
||||
|
||||
let utiArray = UTType.types(tag: "opml", tagClass: .filenameExtension, conformingTo: nil)
|
||||
|
||||
let docPicker = UIDocumentPickerViewController(forOpeningContentTypes: utiArray, asCopy: true)
|
||||
docPicker.delegate = self
|
||||
docPicker.modalPresentationStyle = .formSheet
|
||||
self.present(docPicker, animated: true)
|
||||
}
|
||||
|
||||
func exportOPML(sourceView: UIView, sourceRect: CGRect) {
|
||||
if AccountManager.shared.accounts.count == 1 {
|
||||
opmlAccount = AccountManager.shared.accounts.first!
|
||||
exportOPMLDocumentPicker()
|
||||
} else {
|
||||
exportOPMLAccountPicker(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
}
|
||||
|
||||
func exportOPMLAccountPicker(sourceView: UIView, sourceRect: CGRect) {
|
||||
let title = NSLocalizedString("Choose an account with the subscriptions to export", comment: "Export Account")
|
||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
if let popoverController = alert.popoverPresentationController {
|
||||
popoverController.sourceView = view
|
||||
popoverController.sourceRect = sourceRect
|
||||
}
|
||||
|
||||
for account in AccountManager.shared.sortedAccounts {
|
||||
let action = UIAlertAction(title: account.nameForDisplay, style: .default) { [weak self] action in
|
||||
self?.opmlAccount = account
|
||||
self?.exportOPMLDocumentPicker()
|
||||
}
|
||||
alert.addAction(action)
|
||||
}
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alert.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
|
||||
func exportOPMLDocumentPicker() {
|
||||
guard let account = opmlAccount else { return }
|
||||
|
||||
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
|
||||
let filename = "Subscriptions-\(accountName).opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
|
||||
do {
|
||||
try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
} catch {
|
||||
self.presentError(title: "OPML Export Error", message: error.localizedDescription)
|
||||
logger.error("OPML Export Error: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
let docPicker = UIDocumentPickerViewController(forExporting: [tempFile], asCopy: true)
|
||||
docPicker.modalPresentationStyle = .formSheet
|
||||
self.present(docPicker, animated: true)
|
||||
}
|
||||
|
||||
func openURL(_ urlString: String) {
|
||||
let vc = SFSafariViewController(url: URL(string: urlString)!)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import UserNotifications
|
||||
|
||||
struct SettingsView: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@StateObject private var appDefaults = AppDefaults.shared
|
||||
@StateObject private var viewModel = SettingsViewModel()
|
||||
|
||||
@@ -100,6 +101,9 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .LaunchedFromExternalAction), perform: { _ in
|
||||
dismiss()
|
||||
})
|
||||
.fileImporter(isPresented: $viewModel.showImportView, allowedContentTypes: OPMLDocument.readableContentTypes) { result in
|
||||
switch result {
|
||||
case .success(let url):
|
||||
|
||||
Reference in New Issue
Block a user