mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge branch 'master' of https://github.com/brentsimmons/NetNewsWire
This commit is contained in:
@@ -48,8 +48,8 @@ private extension AccountMetadataFile {
|
||||
if let fileData = try? Data(contentsOf: readURL) {
|
||||
let decoder = PropertyListDecoder()
|
||||
account.metadata = (try? decoder.decode(AccountMetadata.self, from: fileData)) ?? AccountMetadata()
|
||||
account.metadata.delegate = account
|
||||
}
|
||||
account.metadata.delegate = account
|
||||
})
|
||||
|
||||
if let error = errorPointer?.pointee {
|
||||
|
||||
@@ -48,10 +48,10 @@ private extension FeedMetadataFile {
|
||||
if let fileData = try? Data(contentsOf: readURL) {
|
||||
let decoder = PropertyListDecoder()
|
||||
account.feedMetadata = (try? decoder.decode(Account.FeedMetadataDictionary.self, from: fileData)) ?? Account.FeedMetadataDictionary()
|
||||
account.feedMetadata.values.forEach { $0.delegate = account }
|
||||
if !account.startingUp {
|
||||
account.resetFeedMetadataAndUnreadCounts()
|
||||
}
|
||||
}
|
||||
account.feedMetadata.values.forEach { $0.delegate = account }
|
||||
if !account.startingUp {
|
||||
account.resetFeedMetadataAndUnreadCounts()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class ArticleExtractorButton: NSButton {
|
||||
case isError:
|
||||
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractorError, opacity: opacity)
|
||||
case isInProgress:
|
||||
addProgressSublayer(to: hostedLayer)
|
||||
addAnimatedSublayer(to: hostedLayer)
|
||||
default:
|
||||
addImageSublayer(to: hostedLayer, image: AppAssets.articleExtractor, opacity: opacity)
|
||||
}
|
||||
@@ -77,7 +77,7 @@ class ArticleExtractorButton: NSButton {
|
||||
hostedLayer.addSublayer(imageLayer)
|
||||
}
|
||||
|
||||
private func addProgressSublayer(to hostedLayer: CALayer) {
|
||||
private func addAnimatedSublayer(to hostedLayer: CALayer) {
|
||||
let imageProgress1 = AppAssets.articleExtractorProgress1
|
||||
let imageProgress2 = AppAssets.articleExtractorProgress2
|
||||
let imageProgress3 = AppAssets.articleExtractorProgress3
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
|
||||
@@ -81,7 +82,7 @@
|
||||
5170743A232AABFC00A461A3 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; };
|
||||
517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
517630052336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
517630232336657E00E15FFF /* DetailViewControllerWebViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 517630222336657E00E15FFF /* DetailViewControllerWebViewProvider.swift */; };
|
||||
517630232336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */; };
|
||||
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */; };
|
||||
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */; };
|
||||
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */; };
|
||||
@@ -144,7 +145,7 @@
|
||||
51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452722265091600C03939 /* MasterTimelineTableViewCell.swift */; };
|
||||
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452742265091600C03939 /* MasterUnreadIndicatorView.swift */; };
|
||||
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452752265091600C03939 /* MasterTimelineDefaultCellLayout.swift */; };
|
||||
51C4527F2265092C00C03939 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C4527E2265092C00C03939 /* DetailViewController.swift */; };
|
||||
51C4527F2265092C00C03939 /* ArticleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C4527E2265092C00C03939 /* ArticleViewController.swift */; };
|
||||
51C452852265093600C03939 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; };
|
||||
51C452862265093600C03939 /* Add.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51C452822265093600C03939 /* Add.storyboard */; };
|
||||
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452842265093600C03939 /* AddFeedViewController.swift */; };
|
||||
@@ -780,6 +781,7 @@
|
||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
|
||||
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
@@ -819,7 +821,7 @@
|
||||
515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = "<group>"; };
|
||||
51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = "<group>"; };
|
||||
517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = "<group>"; };
|
||||
517630222336657E00E15FFF /* DetailViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewControllerWebViewProvider.swift; sourceTree = "<group>"; };
|
||||
517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerWebViewProvider.swift; sourceTree = "<group>"; };
|
||||
5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = "<group>"; };
|
||||
5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = "<group>"; };
|
||||
5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationProgressView.swift; sourceTree = "<group>"; };
|
||||
@@ -853,7 +855,7 @@
|
||||
51C452722265091600C03939 /* MasterTimelineTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterTimelineTableViewCell.swift; sourceTree = "<group>"; };
|
||||
51C452742265091600C03939 /* MasterUnreadIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterUnreadIndicatorView.swift; sourceTree = "<group>"; };
|
||||
51C452752265091600C03939 /* MasterTimelineDefaultCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterTimelineDefaultCellLayout.swift; sourceTree = "<group>"; };
|
||||
51C4527E2265092C00C03939 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
|
||||
51C4527E2265092C00C03939 /* ArticleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleViewController.swift; sourceTree = "<group>"; };
|
||||
51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlattenedAccountFolderPickerData.swift; sourceTree = "<group>"; };
|
||||
51C452822265093600C03939 /* Add.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Add.storyboard; sourceTree = "<group>"; };
|
||||
51C452842265093600C03939 /* AddFeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFeedViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -1375,13 +1377,14 @@
|
||||
path = Cell;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51C4527D2265092C00C03939 /* Detail */ = {
|
||||
51C4527D2265092C00C03939 /* Article */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51C4527E2265092C00C03939 /* DetailViewController.swift */,
|
||||
517630222336657E00E15FFF /* DetailViewControllerWebViewProvider.swift */,
|
||||
51C4527E2265092C00C03939 /* ArticleViewController.swift */,
|
||||
517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */,
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */,
|
||||
);
|
||||
path = Detail;
|
||||
path = Article;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51C452802265093600C03939 /* Add */ = {
|
||||
@@ -1963,7 +1966,7 @@
|
||||
51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */,
|
||||
51C4525D226508F600C03939 /* MasterFeed */,
|
||||
51C4526D2265091600C03939 /* MasterTimeline */,
|
||||
51C4527D2265092C00C03939 /* Detail */,
|
||||
51C4527D2265092C00C03939 /* Article */,
|
||||
51C452802265093600C03939 /* Add */,
|
||||
5183CCEB227117C70010922C /* Settings */,
|
||||
5183CCDB226F1EEB0010922C /* Progress */,
|
||||
@@ -2160,7 +2163,9 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 840D61A32029031E009BC708 /* Build configuration list for PBXNativeTarget "NetNewsWire-iOS" */;
|
||||
buildPhases = (
|
||||
517D2D80233A46ED00FF3E35 /* Update ArticleExtractorConfig.swift */,
|
||||
840D61782029031C009BC708 /* Sources */,
|
||||
517D2D81233A47AD00FF3E35 /* Reset ArticleExtractorConfig.swift */,
|
||||
840D61792029031C009BC708 /* Frameworks */,
|
||||
840D617A2029031C009BC708 /* Resources */,
|
||||
51C451DF2264C7F200C03939 /* Embed Frameworks */,
|
||||
@@ -2190,8 +2195,9 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 849C647A1ED37A5D003D8FC0 /* Build configuration list for PBXNativeTarget "NetNewsWire" */;
|
||||
buildPhases = (
|
||||
51D6803823330CFF0097A009 /* Update Feedbin Mercury API Keys */,
|
||||
51D6803823330CFF0097A009 /* Update ArticleExtractorConfig.swift */,
|
||||
849C645C1ED37A5D003D8FC0 /* Sources */,
|
||||
517D2D82233A53D600FF3E35 /* Reset ArticleExtractorConfig.swift */,
|
||||
849C645D1ED37A5D003D8FC0 /* Frameworks */,
|
||||
849C645E1ED37A5D003D8FC0 /* Resources */,
|
||||
84C987A52000AC9E0066B150 /* Run Script: Automated build numbers */,
|
||||
@@ -2603,7 +2609,7 @@
|
||||
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";
|
||||
};
|
||||
51D6803823330CFF0097A009 /* Update Feedbin Mercury API Keys */ = {
|
||||
517D2D80233A46ED00FF3E35 /* Update ArticleExtractorConfig.swift */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -2612,14 +2618,68 @@
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Update Feedbin Mercury API Keys";
|
||||
name = "Update ArticleExtractorConfig.swift";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "FAILED=false\n\nif [ \"${CONFIGURATION}\" = \"Debug\" ]; then\necho \"Not a release build. ArticleExtractorConfig.swift not changed.\"\nexit 0\nfi\n\nif [ -z \"${MERCURY_CLIENT_ID}\" ]; then\necho \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift:21:1: warning: Missing Feedbin Mercury Client ID\"\nFAILED=true\nfi\n\nif [ -z \"${MERCURY_CLIENT_SECRET}\" ]; then\necho \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift:22:1: warning: Missing Feedbin Mercury Client Secret\"\nFAILED=true\nfi\n\nsed -i .tmp \"s|{MERCURYID}|${MERCURY_CLIENT_ID}|g; s|{MERCURYSECRET}|${MERCURY_CLIENT_SECRET}|g\" \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift\"\n\nrm -f \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift.tmp\"\n\nif [ \"$FAILED\" = true ]; then\nexit 1\nfi\n\necho \"All env values found!\"\n";
|
||||
shellScript = "FAILED=false\n\nif [ -z \"${MERCURY_CLIENT_ID}\" ]; then\nFAILED=true\nfi\n\nif [ -z \"${MERCURY_CLIENT_SECRET}\" ]; then\nFAILED=true\nfi\n\nif [ \"$FAILED\" = true ]; then\necho \"Missing Feedbin Mercury credetials. ArticleExtractorConfig.swift not changed.\"\nexit 0\nfi\n\nsed -i .tmp \"s|{MERCURYID}|${MERCURY_CLIENT_ID}|g; s|{MERCURYSECRET}|${MERCURY_CLIENT_SECRET}|g\" \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift\"\n\nrm -f \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift.tmp\"\n\necho \"All env values found!\"\n";
|
||||
};
|
||||
517D2D81233A47AD00FF3E35 /* Reset ArticleExtractorConfig.swift */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Reset ArticleExtractorConfig.swift";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "git checkout \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift\"\n";
|
||||
};
|
||||
517D2D82233A53D600FF3E35 /* Reset ArticleExtractorConfig.swift */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Reset ArticleExtractorConfig.swift";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "git checkout \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift\"\n";
|
||||
};
|
||||
51D6803823330CFF0097A009 /* Update ArticleExtractorConfig.swift */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Update ArticleExtractorConfig.swift";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "FAILED=false\n\nif [ -z \"${MERCURY_CLIENT_ID}\" ]; then\nFAILED=true\nfi\n\nif [ -z \"${MERCURY_CLIENT_SECRET}\" ]; then\nFAILED=true\nfi\n\nif [ \"$FAILED\" = true ]; then\necho \"Missing Feedbin Mercury credetials. ArticleExtractorConfig.swift not changed.\"\nexit 0\nfi\n\nsed -i .tmp \"s|{MERCURYID}|${MERCURY_CLIENT_ID}|g; s|{MERCURYSECRET}|${MERCURY_CLIENT_SECRET}|g\" \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift\"\n\nrm -f \"${SRCROOT}/Shared/Article Extractor/ArticleExtractorConfig.swift.tmp\"\n\necho \"All env values found!\"\n\n";
|
||||
};
|
||||
8423E3E3220158E700C3795B /* Run Script: codesign release builds */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -2714,7 +2774,7 @@
|
||||
51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */,
|
||||
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
|
||||
51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */,
|
||||
517630232336657E00E15FFF /* DetailViewControllerWebViewProvider.swift in Sources */,
|
||||
517630232336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift in Sources */,
|
||||
51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */,
|
||||
51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */,
|
||||
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */,
|
||||
@@ -2763,7 +2823,7 @@
|
||||
51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */,
|
||||
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */,
|
||||
51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */,
|
||||
51C4527F2265092C00C03939 /* DetailViewController.swift in Sources */,
|
||||
51C4527F2265092C00C03939 /* ArticleViewController.swift in Sources */,
|
||||
51C4526A226508F600C03939 /* MasterFeedTableViewCellLayout.swift in Sources */,
|
||||
51C452AE2265104D00C03939 /* TimelineStringFormatter.swift in Sources */,
|
||||
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */,
|
||||
@@ -2772,6 +2832,7 @@
|
||||
51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */,
|
||||
51C452AF2265108300C03939 /* ArticleArray.swift in Sources */,
|
||||
51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */,
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */,
|
||||
84CAFCA522BC8C08007694F0 /* FetchRequestQueue.swift in Sources */,
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
|
||||
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
||||
|
||||
@@ -119,8 +119,8 @@ class ActivityManager {
|
||||
}
|
||||
|
||||
func invalidateReading() {
|
||||
nextUnreadActivity?.invalidate()
|
||||
nextUnreadActivity = nil
|
||||
readingActivity?.invalidate()
|
||||
readingActivity = nil
|
||||
}
|
||||
|
||||
static func cleanUp(_ account: Account) {
|
||||
|
||||
@@ -21,12 +21,6 @@ protocol ArticleExtractorDelegate {
|
||||
func articleExtractionDidComplete(extractedArticle: ExtractedArticle)
|
||||
}
|
||||
|
||||
enum ArticleExtractorError: Error {
|
||||
case UnableToParseHTML
|
||||
case MissingURL
|
||||
case UnableToLoadURL
|
||||
}
|
||||
|
||||
class ArticleExtractor {
|
||||
|
||||
private var dataTask: URLSessionDataTask? = nil
|
||||
@@ -41,9 +35,9 @@ class ArticleExtractor {
|
||||
public init?(_ articleLink: String) {
|
||||
self.articleLink = articleLink
|
||||
|
||||
let clientURL = ArticleExtractorConfig.Mercury.clientURL
|
||||
let username = ArticleExtractorConfig.Mercury.clientId
|
||||
let signiture = articleLink.hmacUsingSHA1(key: ArticleExtractorConfig.Mercury.clientSecret)
|
||||
let clientURL = ArticleExtractorConfig.clientURL
|
||||
let username = ArticleExtractorConfig.clientId
|
||||
let signiture = articleLink.hmacUsingSHA1(key: ArticleExtractorConfig.clientSecret)
|
||||
|
||||
if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
|
||||
let fullURL = "\(clientURL)/\(username)/\(signiture)?base64_url=\(base64URL)"
|
||||
@@ -75,7 +69,7 @@ class ArticleExtractor {
|
||||
guard let data = data else {
|
||||
self.state = .failedToParse
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.articleExtractionDidFail(with: ArticleExtractorError.UnableToLoadURL)
|
||||
self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,27 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
enum ArticleExtractorConfig {
|
||||
|
||||
enum Mercury {
|
||||
// For testing add the environment variables in the scheme you are using
|
||||
static let clientId = ArticleExtractorConfig.environmentVariable(named: "MERCURY_CLIENT_ID") ?? Release.mercuryId
|
||||
static let clientSecret = ArticleExtractorConfig.environmentVariable(named: "MERCURY_CLIENT_SECRET") ?? Release.mercurySecret
|
||||
static let clientURL = Release.mercuryURL
|
||||
}
|
||||
|
||||
private enum Release {
|
||||
static let mercuryId = "{MERCURYID}"
|
||||
static let mercurySecret = "{MERCURYSECRET}"
|
||||
static let mercuryURL = "https://extract.feedbin.com/parser"
|
||||
}
|
||||
|
||||
private static func environmentVariable(named: String) -> String? {
|
||||
let processInfo = ProcessInfo.processInfo
|
||||
guard let value = processInfo.environment[named] else {
|
||||
print("‼️ Missing Environment Variable: '\(named)'")
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
static let clientId = "{MERCURYID}"
|
||||
static let clientSecret = "{MERCURYSECRET}"
|
||||
static let clientURL = "https://extract.feedbin.com/parser"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,28 @@ import RSCore
|
||||
|
||||
struct AppAssets {
|
||||
|
||||
static var articleExtractorError: UIImage = {
|
||||
return UIImage(named: "articleExtractorOff")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOff: UIImage = {
|
||||
return UIImage(named: "articleExtractorOff")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOffTinted: UIImage = {
|
||||
let image = UIImage(named: "articleExtractorOff")!
|
||||
return image.maskWithColor(color: AppAssets.primaryAccentColor.cgColor)!
|
||||
}()
|
||||
|
||||
static var articleExtractorOn: UIImage = {
|
||||
return UIImage(named: "articleExtractorOn")!
|
||||
}()
|
||||
|
||||
static var articleExtractorOnTinted: UIImage = {
|
||||
let image = UIImage(named: "articleExtractorOn")!
|
||||
return image.maskWithColor(color: AppAssets.primaryAccentColor.cgColor)!
|
||||
}()
|
||||
|
||||
static var avatarBackgroundColor: UIColor = {
|
||||
return UIColor(named: "avatarBackgroundColor")!
|
||||
}()
|
||||
|
||||
@@ -54,7 +54,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
appDelegate = self
|
||||
|
||||
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
|
||||
let _ = DetailViewControllerWebViewProvider.shared
|
||||
let _ = ArticleViewControllerWebViewProvider.shared
|
||||
AccountManager.shared = AccountManager()
|
||||
AppDefaults.shared = UserDefaults.init(suiteName: "group.\(Bundle.main.bundleIdentifier!)")!
|
||||
|
||||
|
||||
78
iOS/Article/ArticleExtractorButton.swift
Normal file
78
iOS/Article/ArticleExtractorButton.swift
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// ArticleExtractorButton.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/24/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum ArticleExtractorButtonState {
|
||||
case error
|
||||
case animated
|
||||
case on
|
||||
case off
|
||||
}
|
||||
|
||||
class ArticleExtractorButton: UIButton {
|
||||
|
||||
var buttonState: ArticleExtractorButtonState = .off {
|
||||
didSet {
|
||||
if buttonState != oldValue {
|
||||
switch buttonState {
|
||||
case .error:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorError, for: .normal)
|
||||
case .animated:
|
||||
setImage(nil, for: .normal)
|
||||
setNeedsLayout()
|
||||
case .on:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorOn, for: .normal)
|
||||
case .off:
|
||||
stripSublayer()
|
||||
setImage(AppAssets.articleExtractorOff, for: .normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
guard case .animated = buttonState else {
|
||||
return
|
||||
}
|
||||
stripSublayer()
|
||||
addAnimatedSublayer(to: layer)
|
||||
}
|
||||
|
||||
private func stripSublayer() {
|
||||
if layer.sublayers?.count ?? 0 > 1 {
|
||||
layer.sublayers?.last?.removeFromSuperlayer()
|
||||
}
|
||||
}
|
||||
|
||||
private func addAnimatedSublayer(to hostedLayer: CALayer) {
|
||||
let image1 = AppAssets.articleExtractorOffTinted.cgImage!
|
||||
let image2 = AppAssets.articleExtractorOnTinted.cgImage!
|
||||
let images = [image1, image2, image1]
|
||||
|
||||
let imageLayer = CALayer()
|
||||
let imageSize = AppAssets.articleExtractorOff.size
|
||||
imageLayer.bounds = CGRect(x: 0, y: 0, width: imageSize.width, height: imageSize.height)
|
||||
imageLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
|
||||
hostedLayer.addSublayer(imageLayer)
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.calculationMode = CAAnimationCalculationMode.linear
|
||||
animation.keyTimes = [0, 0.5, 1]
|
||||
animation.duration = 2
|
||||
animation.values = images as [Any]
|
||||
animation.repeatCount = HUGE
|
||||
|
||||
imageLayer.add(animation, forKey: "contents")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// DetailViewController.swift
|
||||
// ArticleViewController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/8/19.
|
||||
@@ -12,7 +12,7 @@ import Account
|
||||
import Articles
|
||||
import SafariServices
|
||||
|
||||
enum DetailViewState: Equatable {
|
||||
enum ArticleViewState: Equatable {
|
||||
case noSelection
|
||||
case multipleSelection
|
||||
case loading
|
||||
@@ -20,8 +20,9 @@ enum DetailViewState: Equatable {
|
||||
case extracted(Article, ExtractedArticle)
|
||||
}
|
||||
|
||||
class DetailViewController: UIViewController {
|
||||
class ArticleViewController: UIViewController {
|
||||
|
||||
@IBOutlet private weak var articleExtractorButton: ArticleExtractorButton!
|
||||
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
|
||||
@@ -34,7 +35,7 @@ class DetailViewController: UIViewController {
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
|
||||
var state: DetailViewState = .noSelection {
|
||||
var state: ArticleViewState = .noSelection {
|
||||
didSet {
|
||||
if state != oldValue {
|
||||
updateUI()
|
||||
@@ -54,6 +55,15 @@ class DetailViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
var articleExtractorButtonState: ArticleExtractorButtonState {
|
||||
get {
|
||||
return articleExtractorButton.buttonState
|
||||
}
|
||||
set {
|
||||
articleExtractorButton.buttonState = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private let keyboardManager = KeyboardManager(type: .detail)
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
return keyboardManager.keyCommands
|
||||
@@ -61,7 +71,7 @@ class DetailViewController: UIViewController {
|
||||
|
||||
deinit {
|
||||
webView.removeFromSuperview()
|
||||
DetailViewControllerWebViewProvider.shared.enqueueWebView(webView)
|
||||
ArticleViewControllerWebViewProvider.shared.enqueueWebView(webView)
|
||||
webView = nil
|
||||
}
|
||||
|
||||
@@ -73,7 +83,10 @@ class DetailViewController: UIViewController {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
|
||||
DetailViewControllerWebViewProvider.shared.dequeueWebView() { webView in
|
||||
// For some reason interface builder won't let me set this there.
|
||||
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
|
||||
|
||||
ArticleViewControllerWebViewProvider.shared.dequeueWebView() { webView in
|
||||
|
||||
self.webView = webView
|
||||
self.webViewContainer.addChildAndPin(webView)
|
||||
@@ -95,6 +108,7 @@ class DetailViewController: UIViewController {
|
||||
func updateUI() {
|
||||
|
||||
guard let article = currentArticle else {
|
||||
articleExtractorButton.isEnabled = false
|
||||
nextUnreadBarButtonItem.isEnabled = false
|
||||
prevArticleBarButtonItem.isEnabled = false
|
||||
nextArticleBarButtonItem.isEnabled = false
|
||||
@@ -109,6 +123,7 @@ class DetailViewController: UIViewController {
|
||||
prevArticleBarButtonItem.isEnabled = coordinator.isPrevArticleAvailable
|
||||
nextArticleBarButtonItem.isEnabled = coordinator.isNextArticleAvailable
|
||||
|
||||
articleExtractorButton.isEnabled = true
|
||||
readBarButtonItem.isEnabled = true
|
||||
starBarButtonItem.isEnabled = true
|
||||
browserBarButtonItem.isEnabled = true
|
||||
@@ -178,6 +193,10 @@ class DetailViewController: UIViewController {
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func toggleArticleExtractor(_ sender: Any) {
|
||||
coordinator.toggleArticleExtractor()
|
||||
}
|
||||
|
||||
@IBAction func nextUnread(_ sender: Any) {
|
||||
coordinator.selectNextUnread()
|
||||
}
|
||||
@@ -248,7 +267,7 @@ class DetailViewController: UIViewController {
|
||||
|
||||
// MARK: WKNavigationDelegate
|
||||
|
||||
extension DetailViewController: WKNavigationDelegate {
|
||||
extension ArticleViewController: WKNavigationDelegate {
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
|
||||
if navigationAction.navigationType == .linkActivated {
|
||||
@@ -284,7 +303,7 @@ extension DetailViewController: WKNavigationDelegate {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension DetailViewController {
|
||||
private extension ArticleViewController {
|
||||
|
||||
func updateProgressIndicatorIfNeeded() {
|
||||
if !(UIDevice.current.userInterfaceIdiom == .pad) {
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// DetailViewControllerWebViewProvider.swift
|
||||
// ArticleViewControllerWebViewProvider.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/21/19.
|
||||
@@ -11,9 +11,9 @@ import WebKit
|
||||
|
||||
/// WKWebView has an awful behavior of a flash to white on first load when in dark mode.
|
||||
/// Keep a queue of WebViews where we've already done a trivial load so that by the time we need them in the UI, they're past the flash-to-shite part of their lifecycle.
|
||||
class DetailViewControllerWebViewProvider: NSObject, WKNavigationDelegate {
|
||||
class ArticleViewControllerWebViewProvider: NSObject, WKNavigationDelegate {
|
||||
|
||||
static let shared = DetailViewControllerWebViewProvider()
|
||||
static let shared = ArticleViewControllerWebViewProvider()
|
||||
|
||||
private let minimumQueueDepth = 3
|
||||
private let maximumQueueDepth = 6
|
||||
@@ -7,16 +7,16 @@
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Detail-->
|
||||
<!--Article-->
|
||||
<scene sceneID="yUG-lL-AsK">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="DetailViewController" title="Detail" useStoryboardIdentifierAsRestorationIdentifier="YES" id="JEX-9P-axG" customClass="DetailViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController storyboardIdentifier="ArticleViewController" title="Detail" useStoryboardIdentifierAsRestorationIdentifier="YES" id="JEX-9P-axG" userLabel="Article" customClass="ArticleViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DNb-lt-KzC">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="769"/>
|
||||
<rect key="frame" x="0.0" y="88" width="414" height="725"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@@ -98,10 +98,20 @@
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</toolbarItems>
|
||||
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM"/>
|
||||
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM">
|
||||
<barButtonItem key="rightBarButtonItem" style="plain" id="n8q-YO-ldL">
|
||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="n73-9B-rav" customClass="ArticleExtractorButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="362" y="5" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" image="articleExtractorOff"/>
|
||||
</button>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
||||
<outlet property="articleExtractorButton" destination="n73-9B-rav" id="zud-XU-Kkx"/>
|
||||
<outlet property="browserBarButtonItem" destination="DMh-3X-ebd" id="PkT-Tn-8kG"/>
|
||||
<outlet property="nextArticleBarButtonItem" destination="2qz-M5-Yhk" id="IQd-jx-qEr"/>
|
||||
<outlet property="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="xJr-5y-p1N"/>
|
||||
@@ -152,6 +162,7 @@
|
||||
</barButtonItem>
|
||||
</toolbarItems>
|
||||
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" id="wcC-1L-ug4"/>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="firstUnreadButton" destination="2v2-jD-C9k" id="8NP-Uc-3Fn"/>
|
||||
@@ -203,7 +214,10 @@
|
||||
</barButtonItem>
|
||||
</toolbarItems>
|
||||
<navigationItem key="navigationItem" title="Feeds" id="Zdf-7t-Un8">
|
||||
<barButtonItem key="leftBarButtonItem" title="Item" image="gear" catalog="system" id="TlU-Pg-ATe">
|
||||
<barButtonItem key="leftBarButtonItem" title="Settings" image="gear" catalog="system" id="TlU-Pg-ATe">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Settings"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="settings:" destination="7bK-jq-Zjz" id="Y8a-lz-Im7"/>
|
||||
</connections>
|
||||
@@ -225,6 +239,7 @@
|
||||
<image name="arrow.down" catalog="system" width="58" height="64"/>
|
||||
<image name="arrow.down.circle" catalog="system" width="64" height="62"/>
|
||||
<image name="arrow.up" catalog="system" width="58" height="64"/>
|
||||
<image name="articleExtractorOff" width="18" height="23"/>
|
||||
<image name="circle" catalog="system" width="64" height="62"/>
|
||||
<image name="gear" catalog="system" width="64" height="60"/>
|
||||
<image name="safari" catalog="system" width="64" height="62"/>
|
||||
|
||||
BIN
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/ArticleExtractorOff.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/ArticleExtractorOff.pdf
vendored
Normal file
Binary file not shown.
15
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/Contents.json
vendored
Normal file
15
iOS/Resources/Assets.xcassets/articleExtractorError.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOff.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/ArticleExtractorOff.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/ArticleExtractorOff.pdf
vendored
Normal file
Binary file not shown.
15
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/Contents.json
vendored
Normal file
15
iOS/Resources/Assets.xcassets/articleExtractorOff.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOff.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
BIN
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/ArticleExtractorOn.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/ArticleExtractorOn.pdf
vendored
Normal file
Binary file not shown.
15
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/Contents.json
vendored
Normal file
15
iOS/Resources/Assets.xcassets/articleExtractorOn.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ArticleExtractorOn.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
|
||||
private var activityManager = ActivityManager()
|
||||
|
||||
private var isShowingExtractedArticle = false
|
||||
private var articleExtractor: ArticleExtractor? = nil
|
||||
|
||||
private var rootSplitViewController: RootSplitViewController!
|
||||
private var masterNavigationController: UINavigationController!
|
||||
private var masterFeedViewController: MasterFeedViewController!
|
||||
@@ -36,17 +39,17 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
return rootSplitViewController.children.last as? UISplitViewController
|
||||
}
|
||||
|
||||
private var detailViewController: DetailViewController? {
|
||||
if let detail = masterNavigationController.viewControllers.last as? DetailViewController {
|
||||
private var articleViewController: ArticleViewController? {
|
||||
if let detail = masterNavigationController.viewControllers.last as? ArticleViewController {
|
||||
return detail
|
||||
}
|
||||
if let subSplit = subSplitViewController {
|
||||
if let navController = subSplit.viewControllers.last as? UINavigationController {
|
||||
return navController.topViewController as? DetailViewController
|
||||
return navController.topViewController as? ArticleViewController
|
||||
}
|
||||
} else {
|
||||
if let navController = rootSplitViewController.viewControllers.last as? UINavigationController {
|
||||
return navController.topViewController as? DetailViewController
|
||||
return navController.topViewController as? ArticleViewController
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -289,9 +292,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
masterFeedViewController.coordinator = self
|
||||
masterNavigationController.pushViewController(masterFeedViewController, animated: false)
|
||||
|
||||
let detailViewController = UIStoryboard.main.instantiateController(ofType: DetailViewController.self)
|
||||
detailViewController.coordinator = self
|
||||
let detailNavigationController = addNavControllerIfNecessary(detailViewController, showButton: false)
|
||||
let articleViewController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
||||
articleViewController.coordinator = self
|
||||
let detailNavigationController = addNavControllerIfNecessary(articleViewController, showButton: false)
|
||||
rootSplitViewController.showDetailViewController(detailNavigationController, sender: self)
|
||||
|
||||
configureThreePanelMode(for: size)
|
||||
@@ -553,32 +556,37 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
func selectArticle(_ article: Article?, automated: Bool = true) {
|
||||
guard article != currentArticle else { return }
|
||||
|
||||
articleExtractor?.cancel()
|
||||
articleExtractor = nil
|
||||
isShowingExtractedArticle = false
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
|
||||
currentArticle = article
|
||||
activityManager.reading(currentArticle)
|
||||
|
||||
if article == nil {
|
||||
if rootSplitViewController.isCollapsed {
|
||||
if masterNavigationController.children.last is DetailViewController {
|
||||
if masterNavigationController.children.last is ArticleViewController {
|
||||
masterNavigationController.popViewController(animated: !automated)
|
||||
}
|
||||
} else {
|
||||
detailViewController?.state = .noSelection
|
||||
articleViewController?.state = .noSelection
|
||||
}
|
||||
masterTimelineViewController?.updateArticleSelection(animate: !automated)
|
||||
return
|
||||
}
|
||||
|
||||
if detailViewController == nil {
|
||||
let detailViewController = UIStoryboard.main.instantiateController(ofType: DetailViewController.self)
|
||||
detailViewController.coordinator = self
|
||||
installDetailController(detailViewController, automated: automated)
|
||||
if articleViewController == nil {
|
||||
let articleViewController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
||||
articleViewController.coordinator = self
|
||||
installArticleController(articleViewController, automated: automated)
|
||||
}
|
||||
|
||||
if automated {
|
||||
masterTimelineViewController?.updateArticleSelection(animate: false)
|
||||
}
|
||||
|
||||
detailViewController?.state = .article(article!)
|
||||
articleViewController?.state = .article(article!)
|
||||
|
||||
if let article = currentArticle {
|
||||
markArticles(Set([article]), statusKey: .read, flag: true)
|
||||
@@ -686,8 +694,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
}
|
||||
|
||||
func scrollOrGoToNextUnread() {
|
||||
if detailViewController?.canScrollDown() ?? false {
|
||||
detailViewController?.scrollPageDown()
|
||||
if articleViewController?.canScrollDown() ?? false {
|
||||
articleViewController?.scrollPageDown()
|
||||
} else {
|
||||
selectNextUnread()
|
||||
}
|
||||
@@ -795,6 +803,40 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
masterFeedViewController.present(addViewController, animated: true)
|
||||
}
|
||||
|
||||
func toggleArticleExtractor() {
|
||||
|
||||
guard let article = currentArticle else {
|
||||
return
|
||||
}
|
||||
|
||||
guard articleExtractor?.state != .processing else {
|
||||
articleExtractor?.cancel()
|
||||
articleExtractor = nil
|
||||
isShowingExtractedArticle = false
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
articleViewController?.state = .article(article)
|
||||
return
|
||||
}
|
||||
|
||||
guard !isShowingExtractedArticle else {
|
||||
isShowingExtractedArticle = false
|
||||
articleViewController?.articleExtractorButtonState = .off
|
||||
articleViewController?.state = .article(article)
|
||||
return
|
||||
}
|
||||
|
||||
if let articleExtractor = articleExtractor, let extractedArticle = articleExtractor.article {
|
||||
if currentArticle?.preferredLink == articleExtractor.articleLink {
|
||||
isShowingExtractedArticle = true
|
||||
articleViewController?.articleExtractorButtonState = .on
|
||||
articleViewController?.state = .extracted(article, extractedArticle)
|
||||
}
|
||||
} else {
|
||||
startArticleExtractorForCurrentLink()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func homePageURLForFeed(_ indexPath: IndexPath) -> URL? {
|
||||
guard let node = nodeFor(indexPath),
|
||||
let feed = node.representedObject as? Feed,
|
||||
@@ -844,7 +886,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
}
|
||||
|
||||
func navigateToDetail() {
|
||||
detailViewController?.focus()
|
||||
articleViewController?.focus()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -880,6 +922,24 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
||||
|
||||
}
|
||||
|
||||
// MARK: ArticleExtractorDelegate
|
||||
|
||||
extension SceneCoordinator: ArticleExtractorDelegate {
|
||||
|
||||
func articleExtractionDidFail(with: Error) {
|
||||
articleViewController?.articleExtractorButtonState = .error
|
||||
}
|
||||
|
||||
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
|
||||
if let article = currentArticle, articleExtractor?.state != .cancelled {
|
||||
isShowingExtractedArticle = true
|
||||
articleViewController?.state = .extracted(article, extractedArticle)
|
||||
articleViewController?.articleExtractorButtonState = .on
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension SceneCoordinator {
|
||||
@@ -1181,6 +1241,15 @@ private extension SceneCoordinator {
|
||||
|
||||
// MARK: Fetching Articles
|
||||
|
||||
func startArticleExtractorForCurrentLink() {
|
||||
if let link = currentArticle?.preferredLink, let extractor = ArticleExtractor(link) {
|
||||
extractor.delegate = self
|
||||
extractor.process()
|
||||
articleExtractor = extractor
|
||||
articleViewController?.articleExtractorButtonState = .animated
|
||||
}
|
||||
}
|
||||
|
||||
func emptyTheTimeline() {
|
||||
if !articles.isEmpty {
|
||||
replaceArticles(with: Set<Article>(), animate: true)
|
||||
@@ -1340,16 +1409,16 @@ private extension SceneCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
func installDetailController(_ detailController: UIViewController, automated: Bool) {
|
||||
func installArticleController(_ articleController: UIViewController, automated: Bool) {
|
||||
|
||||
if let subSplit = subSplitViewController {
|
||||
let controller = addNavControllerIfNecessary(detailController, showButton: false)
|
||||
let controller = addNavControllerIfNecessary(articleController, showButton: false)
|
||||
subSplit.showDetailViewController(controller, sender: self)
|
||||
} else if rootSplitViewController.isCollapsed {
|
||||
let controller = addNavControllerIfNecessary(detailController, showButton: false)
|
||||
let controller = addNavControllerIfNecessary(articleController, showButton: false)
|
||||
masterNavigationController.pushViewController(controller, animated: !automated)
|
||||
} else {
|
||||
let controller = addNavControllerIfNecessary(detailController, showButton: true)
|
||||
let controller = addNavControllerIfNecessary(articleController, showButton: true)
|
||||
rootSplitViewController.showDetailViewController(controller, sender: self)
|
||||
}
|
||||
|
||||
@@ -1406,12 +1475,12 @@ private extension SceneCoordinator {
|
||||
}
|
||||
|
||||
let controller: UIViewController = {
|
||||
if let result = detailViewController {
|
||||
if let result = articleViewController {
|
||||
return result
|
||||
} else {
|
||||
let detailController = UIStoryboard.main.instantiateController(ofType: DetailViewController.self)
|
||||
detailController.coordinator = self
|
||||
return detailController
|
||||
let articleViewController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
||||
articleViewController.coordinator = self
|
||||
return articleViewController
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ PROVISIONING_PROFILE_SPECIFIER =
|
||||
// /Users/Shared/git/SharedXcodeSettings/DeveloperSettings.xcconfig
|
||||
//
|
||||
|
||||
#include? "../../SharedXcodeSettings/ProjectSettings.xcconfig"
|
||||
#include? "../../SharedXcodeSettings/DeveloperSettings.xcconfig"
|
||||
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include? "../../SharedXcodeSettings/ProjectSettings.xcconfig"
|
||||
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
||||
CLANG_ANALYZER_NONNULL = YES
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include? "../../SharedXcodeSettings/ReleaseSettings.xcconfig"
|
||||
#include "./NetNewsWire_project.xcconfig"
|
||||
|
||||
COPY_PHASE_STRIP = NO
|
||||
|
||||
Reference in New Issue
Block a user