Merge pull request #1278 from kielgillard/master
Use ASWebAuthenticationSession to obtain OAuth grant for Feedly production and sandbox.
This commit is contained in:
commit
cd50e7c88f
@ -308,7 +308,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func oauthAuthorizationClient(for type: AccountType) -> OAuthAuthorizationClient {
|
internal static func oauthAuthorizationClient(for type: AccountType) -> OAuthAuthorizationClient {
|
||||||
|
switch type {
|
||||||
|
case .feedly:
|
||||||
|
return FeedlyAccountDelegate.environment.oauthAuthorizationClient
|
||||||
|
default:
|
||||||
|
fatalError("\(type) is not a client for OAuth authorization code granting.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func oauthAuthorizationCodeGrantRequest(for type: AccountType) -> URLRequest {
|
||||||
let grantingType: OAuthAuthorizationGranting.Type
|
let grantingType: OAuthAuthorizationGranting.Type
|
||||||
switch type {
|
switch type {
|
||||||
case .feedly:
|
case .feedly:
|
||||||
@ -317,19 +326,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
fatalError("\(type) does not support OAuth authorization code granting.")
|
fatalError("\(type) does not support OAuth authorization code granting.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return grantingType.oauthAuthorizationClient
|
return grantingType.oauthAuthorizationCodeGrantRequest()
|
||||||
}
|
|
||||||
|
|
||||||
public static func oauthAuthorizationCodeGrantRequest(for type: AccountType, client: OAuthAuthorizationClient) -> URLRequest {
|
|
||||||
let grantingType: OAuthAuthorizationGranting.Type
|
|
||||||
switch type {
|
|
||||||
case .feedly:
|
|
||||||
grantingType = FeedlyAccountDelegate.self
|
|
||||||
default:
|
|
||||||
fatalError("\(type) does not support OAuth authorization code granting.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return grantingType.oauthAuthorizationCodeGrantRequest(for: client)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse,
|
public static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse,
|
||||||
@ -346,7 +343,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
fatalError("\(accountType) does not support OAuth authorization code granting.")
|
fatalError("\(accountType) does not support OAuth authorization code granting.")
|
||||||
}
|
}
|
||||||
|
|
||||||
grantingType.requestOAuthAccessToken(with: response, client: client, transport: transport, completionHandler: completionHandler)
|
grantingType.requestOAuthAccessToken(with: response, transport: transport, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshAll(completion: @escaping (Result<Void, Error>) -> Void) {
|
public func refreshAll(completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
@ -120,6 +120,8 @@
|
|||||||
9E85C8E82366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8E72366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift */; };
|
9E85C8E82366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8E72366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift */; };
|
||||||
9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8E9236700AD00D0F1F7 /* FeedlyGetEntriesOperation.swift */; };
|
9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8E9236700AD00D0F1F7 /* FeedlyGetEntriesOperation.swift */; };
|
||||||
9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8EC2367020700D0F1F7 /* FeedlyGetEntriesService.swift */; };
|
9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E85C8EC2367020700D0F1F7 /* FeedlyGetEntriesService.swift */; };
|
||||||
|
9E964EB823754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E964E9E23754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift */; };
|
||||||
|
9E964EBA23754B4000A7AF2E /* OAuthAccountAuthorizationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E964EB923754B4000A7AF2E /* OAuthAccountAuthorizationOperation.swift */; };
|
||||||
9EA3133B231E368100268BA0 /* FeedlyAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */; };
|
9EA3133B231E368100268BA0 /* FeedlyAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */; };
|
||||||
9EAEC60C2332FE830085D7C9 /* FeedlyCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */; };
|
9EAEC60C2332FE830085D7C9 /* FeedlyCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */; };
|
||||||
9EAEC60E2332FEC20085D7C9 /* FeedlyFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */; };
|
9EAEC60E2332FEC20085D7C9 /* FeedlyFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */; };
|
||||||
@ -320,6 +322,8 @@
|
|||||||
9E85C8E72366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGetPagedStreamContentsService.swift; sourceTree = "<group>"; };
|
9E85C8E72366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGetPagedStreamContentsService.swift; sourceTree = "<group>"; };
|
||||||
9E85C8E9236700AD00D0F1F7 /* FeedlyGetEntriesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetEntriesOperation.swift; sourceTree = "<group>"; };
|
9E85C8E9236700AD00D0F1F7 /* FeedlyGetEntriesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetEntriesOperation.swift; sourceTree = "<group>"; };
|
||||||
9E85C8EC2367020700D0F1F7 /* FeedlyGetEntriesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetEntriesService.swift; sourceTree = "<group>"; };
|
9E85C8EC2367020700D0F1F7 /* FeedlyGetEntriesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetEntriesService.swift; sourceTree = "<group>"; };
|
||||||
|
9E964E9E23754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OAuthAuthorizationClient+Feedly.swift"; sourceTree = "<group>"; };
|
||||||
|
9E964EB923754B4000A7AF2E /* OAuthAccountAuthorizationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAccountAuthorizationOperation.swift; sourceTree = "<group>"; };
|
||||||
9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegate.swift; sourceTree = "<group>"; };
|
9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegate.swift; sourceTree = "<group>"; };
|
||||||
9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollection.swift; sourceTree = "<group>"; };
|
9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollection.swift; sourceTree = "<group>"; };
|
||||||
9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeed.swift; sourceTree = "<group>"; };
|
9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeed.swift; sourceTree = "<group>"; };
|
||||||
@ -616,7 +620,9 @@
|
|||||||
9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */,
|
9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */,
|
||||||
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */,
|
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */,
|
||||||
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */,
|
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */,
|
||||||
|
9E964E9E23754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift */,
|
||||||
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */,
|
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */,
|
||||||
|
9E964EB923754B4000A7AF2E /* OAuthAccountAuthorizationOperation.swift */,
|
||||||
9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */,
|
9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */,
|
||||||
9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */,
|
9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */,
|
||||||
9E510D6D234F16A8002E6F1A /* FeedlyAddFeedRequest.swift */,
|
9E510D6D234F16A8002E6F1A /* FeedlyAddFeedRequest.swift */,
|
||||||
@ -716,7 +722,9 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 8489350A1F62485000CEBD24 /* Build configuration list for PBXNativeTarget "Account" */;
|
buildConfigurationList = 8489350A1F62485000CEBD24 /* Build configuration list for PBXNativeTarget "Account" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
9E964EBB2375512300A7AF2E /* Run Script: Update OAuthAuthorizationClient+Feedly.swift */,
|
||||||
848934F11F62484F00CEBD24 /* Sources */,
|
848934F11F62484F00CEBD24 /* Sources */,
|
||||||
|
9E964EBC2375517100A7AF2E /* Run Script: Reset OAuthAuthorizationClient+Feedly.swift */,
|
||||||
848934F21F62484F00CEBD24 /* Frameworks */,
|
848934F21F62484F00CEBD24 /* Frameworks */,
|
||||||
848934F31F62484F00CEBD24 /* Headers */,
|
848934F31F62484F00CEBD24 /* Headers */,
|
||||||
848934F41F62484F00CEBD24 /* Resources */,
|
848934F41F62484F00CEBD24 /* Resources */,
|
||||||
@ -889,6 +897,42 @@
|
|||||||
shellPath = /bin/sh;
|
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";
|
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";
|
||||||
};
|
};
|
||||||
|
9E964EBB2375512300A7AF2E /* Run Script: Update OAuthAuthorizationClient+Feedly.swift */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script: Update OAuthAuthorizationClient+Feedly.swift";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "FAILED=false\n\nif [ -z \"${FEEDLY_CLIENT_ID}\" ]; then\necho \"Missing Feedly Client ID\"\nFAILED=true\nfi\n\nif [ -z \"${FEEDLY_CLIENT_SECRET}\" ]; then\necho \"Missing Feedly Client Secret\"\nFAILED=true\nfi\n\nFEEDLY_CLIENT_SOURCE=\"${SRCROOT}/Feedly/OAuthAuthorizationClient+Feedly.swift\"\n\nif [ \"$FAILED\" = true ]; then\necho \"Missing Feedly client ID or secret. ${FEEDLY_CLIENT_SOURCE} not changed.\"\nexit 0\nfi\n\n# echo \"Substituting variables in: ${FEEDLY_CLIENT_SOURCE}\"\n\nif [ -e \"${FEEDLY_CLIENT_SOURCE}\" ]\nthen\n sed -i .tmp \"s|{FEEDLY_CLIENT_ID}|${FEEDLY_CLIENT_ID}|g; s|{FEEDLY_CLIENT_SECRET}|${FEEDLY_CLIENT_SECRET}|g\" $FEEDLY_CLIENT_SOURCE\n # echo \"`git diff ${FEEDLY_CLIENT_SOURCE}`\"\n rm -f \"${FEEDLY_CLIENT_SOURCE}.tmp\"\nelse\n echo \"File does not exist at ${FEEDLY_CLIENT_SOURCE}. Has it been moved or renamed?\"\n exit -1\nfi\n\necho \"All env values found!\"\n";
|
||||||
|
};
|
||||||
|
9E964EBC2375517100A7AF2E /* Run Script: Reset OAuthAuthorizationClient+Feedly.swift */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run Script: Reset OAuthAuthorizationClient+Feedly.swift";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "git checkout \"${SRCROOT}/Feedly/OAuthAuthorizationClient+Feedly.swift\"\n";
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@ -932,6 +976,7 @@
|
|||||||
9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */,
|
9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */,
|
||||||
9E1D154D233370D800F4944C /* FeedlySyncAllOperation.swift in Sources */,
|
9E1D154D233370D800F4944C /* FeedlySyncAllOperation.swift in Sources */,
|
||||||
844B297D2106C7EC004020B3 /* Feed.swift in Sources */,
|
844B297D2106C7EC004020B3 /* Feed.swift in Sources */,
|
||||||
|
9E964EBA23754B4000A7AF2E /* OAuthAccountAuthorizationOperation.swift in Sources */,
|
||||||
9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */,
|
9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */,
|
||||||
9E1D15512334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift in Sources */,
|
9E1D15512334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift in Sources */,
|
||||||
9E1773D7234575AB0056A5A8 /* FeedlyTag.swift in Sources */,
|
9E1773D7234575AB0056A5A8 /* FeedlyTag.swift in Sources */,
|
||||||
@ -972,6 +1017,7 @@
|
|||||||
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
||||||
9E1773D32345700F0056A5A8 /* FeedlyLink.swift in Sources */,
|
9E1773D32345700F0056A5A8 /* FeedlyLink.swift in Sources */,
|
||||||
9EAEC62823331C350085D7C9 /* FeedlyCategory.swift in Sources */,
|
9EAEC62823331C350085D7C9 /* FeedlyCategory.swift in Sources */,
|
||||||
|
9E964EB823754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift in Sources */,
|
||||||
9EF1B10923590E93000A486A /* FeedlyStreamIds.swift in Sources */,
|
9EF1B10923590E93000A486A /* FeedlyStreamIds.swift in Sources */,
|
||||||
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
|
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
|
||||||
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */,
|
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */,
|
||||||
|
@ -130,7 +130,7 @@ class FeedlySyncAllOperationTests: XCTestCase {
|
|||||||
|
|
||||||
OperationQueue.main.addOperation(syncAll)
|
OperationQueue.main.addOperation(syncAll)
|
||||||
|
|
||||||
waitForExpectations(timeout: 2)
|
waitForExpectations(timeout: 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func performInitialSync() {
|
func performInitialSync() {
|
||||||
|
@ -31,20 +31,10 @@ final class FeedlyAPICaller {
|
|||||||
|
|
||||||
var oauthAuthorizationClient: OAuthAuthorizationClient {
|
var oauthAuthorizationClient: OAuthAuthorizationClient {
|
||||||
switch self {
|
switch self {
|
||||||
case .cloud:
|
|
||||||
/// Models private NetNewsWire client secrets.
|
|
||||||
/// https://developer.feedly.com/v3/auth/#authenticating-a-user-and-obtaining-an-auth-code
|
|
||||||
return OAuthAuthorizationClient(id: "{FEEDLY-ID}",
|
|
||||||
redirectUri: "{FEEDLY-REDIRECT-URI}",
|
|
||||||
state: nil,
|
|
||||||
secret: "{FEEDLY-SECRET}")
|
|
||||||
case .sandbox:
|
case .sandbox:
|
||||||
/// Models public sandbox API values found at:
|
return .feedlySandboxClient
|
||||||
/// https://groups.google.com/forum/#!topic/feedly-cloud/WwQWMgDmOuw
|
case .cloud:
|
||||||
return OAuthAuthorizationClient(id: "sandbox",
|
return .feedlyCloudClient
|
||||||
redirectUri: "http://localhost",
|
|
||||||
state: nil,
|
|
||||||
secret: "ReVGXA6WekanCxbf")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,8 @@ extension FeedlyAccountDelegate: OAuthAuthorizationGranting {
|
|||||||
|
|
||||||
private static let oauthAuthorizationGrantScope = "https://cloud.feedly.com/subscriptions"
|
private static let oauthAuthorizationGrantScope = "https://cloud.feedly.com/subscriptions"
|
||||||
|
|
||||||
static var oauthAuthorizationClient: OAuthAuthorizationClient {
|
static func oauthAuthorizationCodeGrantRequest() -> URLRequest {
|
||||||
return environment.oauthAuthorizationClient
|
let client = environment.oauthAuthorizationClient
|
||||||
}
|
|
||||||
|
|
||||||
static func oauthAuthorizationCodeGrantRequest(for client: OAuthAuthorizationClient) -> URLRequest {
|
|
||||||
let authorizationRequest = OAuthAuthorizationRequest(clientId: client.id,
|
let authorizationRequest = OAuthAuthorizationRequest(clientId: client.id,
|
||||||
redirectUri: client.redirectUri,
|
redirectUri: client.redirectUri,
|
||||||
scope: oauthAuthorizationGrantScope,
|
scope: oauthAuthorizationGrantScope,
|
||||||
@ -40,7 +37,8 @@ extension FeedlyAccountDelegate: OAuthAuthorizationGranting {
|
|||||||
return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents)
|
return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, client: OAuthAuthorizationClient, transport: Transport, completionHandler: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) {
|
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, completionHandler: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) {
|
||||||
|
let client = environment.oauthAuthorizationClient
|
||||||
let request = OAuthAccessTokenRequest(authorizationResponse: response,
|
let request = OAuthAccessTokenRequest(authorizationResponse: response,
|
||||||
scope: oauthAuthorizationGrantScope,
|
scope: oauthAuthorizationGrantScope,
|
||||||
client: client)
|
client: client)
|
||||||
|
@ -17,6 +17,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
/// Feedly has a sandbox API and a production API.
|
/// Feedly has a sandbox API and a production API.
|
||||||
/// This property is referred to when clients need to know which environment it should be pointing to.
|
/// This property is referred to when clients need to know which environment it should be pointing to.
|
||||||
|
/// The value of this proptery must match any `OAuthAuthorizationClient` used.
|
||||||
static var environment: FeedlyAPICaller.API {
|
static var environment: FeedlyAPICaller.API {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// https://developer.feedly.com/v3/developer/
|
// https://developer.feedly.com/v3/developer/
|
||||||
@ -24,7 +25,6 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
return .cloud
|
return .cloud
|
||||||
}
|
}
|
||||||
return .sandbox
|
return .sandbox
|
||||||
|
|
||||||
#else
|
#else
|
||||||
return .cloud
|
return .cloud
|
||||||
#endif
|
#endif
|
||||||
@ -53,6 +53,8 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let oauthAuthorizationClient: OAuthAuthorizationClient
|
||||||
|
|
||||||
var accountMetadata: AccountMetadata?
|
var accountMetadata: AccountMetadata?
|
||||||
|
|
||||||
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||||
@ -97,6 +99,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3")
|
let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3")
|
||||||
self.database = SyncDatabase(databaseFilePath: databaseFilePath)
|
self.database = SyncDatabase(databaseFilePath: databaseFilePath)
|
||||||
|
self.oauthAuthorizationClient = api.oauthAuthorizationClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Account API
|
// MARK: Account API
|
||||||
@ -484,8 +487,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
func accountDidInitialize(_ account: Account) {
|
func accountDidInitialize(_ account: Account) {
|
||||||
credentials = try? account.retrieveCredentials(type: .oauthAccessToken)
|
credentials = try? account.retrieveCredentials(type: .oauthAccessToken)
|
||||||
|
|
||||||
let client = FeedlyAccountDelegate.oauthAuthorizationClient
|
let refreshAccessToken = FeedlyRefreshAccessTokenOperation(account: account, service: self, oauthClient: oauthAuthorizationClient, log: log)
|
||||||
let refreshAccessToken = FeedlyRefreshAccessTokenOperation(account: account, service: self, oauthClient: client, log: log)
|
|
||||||
operationQueue.addOperation(refreshAccessToken)
|
operationQueue.addOperation(refreshAccessToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,184 @@
|
|||||||
|
//
|
||||||
|
// OAuthAccountAuthorizationOperation.swift
|
||||||
|
// NetNewsWire
|
||||||
|
//
|
||||||
|
// Created by Kiel Gillard on 8/11/19.
|
||||||
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AuthenticationServices
|
||||||
|
|
||||||
|
public protocol OAuthAccountAuthorizationOperationDelegate: class {
|
||||||
|
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class OAuthAccountAuthorizationOperation: Operation, ASWebAuthenticationPresentationContextProviding {
|
||||||
|
|
||||||
|
public weak var presentationAnchor: ASPresentationAnchor?
|
||||||
|
public weak var delegate: OAuthAccountAuthorizationOperationDelegate?
|
||||||
|
|
||||||
|
private let accountType: AccountType
|
||||||
|
private let oauthClient: OAuthAuthorizationClient
|
||||||
|
private var session: ASWebAuthenticationSession?
|
||||||
|
|
||||||
|
public init(accountType: AccountType) {
|
||||||
|
self.accountType = accountType
|
||||||
|
self.oauthClient = Account.oauthAuthorizationClient(for: accountType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func main() {
|
||||||
|
assert(Thread.isMainThread)
|
||||||
|
assert(presentationAnchor != nil, "\(self) outlived presentation anchor.")
|
||||||
|
|
||||||
|
guard !isCancelled else {
|
||||||
|
didFinish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = Account.oauthAuthorizationCodeGrantRequest(for: accountType)
|
||||||
|
|
||||||
|
guard let url = request.url else {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
self.didEndAuthentication(url: nil, error: URLError(.badURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let redirectUri = URL(string: oauthClient.redirectUri), let scheme = redirectUri.scheme else {
|
||||||
|
assertionFailure("Could not get callback URL scheme from \(oauthClient.redirectUri)")
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
self.didEndAuthentication(url: nil, error: URLError(.badURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme) { url, error in
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.didEndAuthentication(url: url, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.session = session
|
||||||
|
session.presentationContextProvider = self
|
||||||
|
|
||||||
|
session.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func cancel() {
|
||||||
|
session?.cancel()
|
||||||
|
super.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func didEndAuthentication(url: URL?, error: Error?) {
|
||||||
|
guard !isCancelled else {
|
||||||
|
didFinish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
guard let url = url else {
|
||||||
|
if let error = error {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
throw URLError(.badURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = try OAuthAuthorizationResponse(url: url, client: oauthClient)
|
||||||
|
|
||||||
|
Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, completionHandler: didEndRequestingAccessToken(_:))
|
||||||
|
|
||||||
|
} catch is ASWebAuthenticationSessionError {
|
||||||
|
didFinish() // Primarily, cancellation.
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
|
||||||
|
guard let anchor = presentationAnchor else {
|
||||||
|
fatalError("\(self) has outlived presentation anchor.")
|
||||||
|
}
|
||||||
|
return anchor
|
||||||
|
}
|
||||||
|
|
||||||
|
private func didEndRequestingAccessToken(_ result: Result<OAuthAuthorizationGrant, Error>) {
|
||||||
|
guard !isCancelled else {
|
||||||
|
didFinish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case .success(let tokenResponse):
|
||||||
|
saveAccount(for: tokenResponse)
|
||||||
|
case .failure(let error):
|
||||||
|
didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveAccount(for grant: OAuthAuthorizationGrant) {
|
||||||
|
// TODO: Find an already existing account for this username?
|
||||||
|
let account = AccountManager.shared.createAccount(type: .feedly)
|
||||||
|
do {
|
||||||
|
|
||||||
|
// Store the refresh token first because it sends this token to the account delegate.
|
||||||
|
if let token = grant.refreshToken {
|
||||||
|
try account.storeCredentials(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now store the access token because we want the account delegate to use it.
|
||||||
|
try account.storeCredentials(grant.accessToken)
|
||||||
|
|
||||||
|
didFinish()
|
||||||
|
} catch {
|
||||||
|
didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Managing Operation State
|
||||||
|
|
||||||
|
private func didFinish() {
|
||||||
|
assert(Thread.isMainThread)
|
||||||
|
assert(!isFinished, "Finished operation is attempting to finish again.")
|
||||||
|
self.isExecutingOperation = false
|
||||||
|
self.isFinishedOperation = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func didFinish(_ error: Error) {
|
||||||
|
assert(Thread.isMainThread)
|
||||||
|
assert(!isFinished, "Finished operation is attempting to finish again.")
|
||||||
|
delegate?.oauthAccountAuthorizationOperation(self, didFailWith: error)
|
||||||
|
didFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func start() {
|
||||||
|
isExecutingOperation = true
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.main()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var isExecuting: Bool {
|
||||||
|
return isExecutingOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isExecutingOperation = false {
|
||||||
|
willSet {
|
||||||
|
willChangeValue(for: \.isExecuting)
|
||||||
|
}
|
||||||
|
didSet {
|
||||||
|
didChangeValue(for: \.isExecuting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public var isFinished: Bool {
|
||||||
|
return isFinishedOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isFinishedOperation = false {
|
||||||
|
willSet {
|
||||||
|
willChangeValue(for: \.isFinished)
|
||||||
|
}
|
||||||
|
didSet {
|
||||||
|
didChangeValue(for: \.isFinished)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// OAuthAuthorizationClient+NetNewsWire.swift
|
||||||
|
// Account
|
||||||
|
//
|
||||||
|
// Created by Kiel Gillard on 8/11/19.
|
||||||
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension OAuthAuthorizationClient {
|
||||||
|
|
||||||
|
static var feedlyCloudClient: OAuthAuthorizationClient {
|
||||||
|
/// Models private NetNewsWire client secrets.
|
||||||
|
/// These placeholders are substitued at build time using a Run Script phase with build settings.
|
||||||
|
/// https://developer.feedly.com/v3/auth/#authenticating-a-user-and-obtaining-an-auth-code
|
||||||
|
return OAuthAuthorizationClient(id: "{FEEDLY_CLIENT_ID}",
|
||||||
|
redirectUri: "netnewswire://auth/feedly",
|
||||||
|
state: nil,
|
||||||
|
secret: "{FEEDLY_CLIENT_SECRET}")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var feedlySandboxClient: OAuthAuthorizationClient {
|
||||||
|
/// We use this funky redirect URI because ASWebAuthenticationSession will try to load http://localhost URLs.
|
||||||
|
/// See https://developer.feedly.com/v3/sandbox/ for more information.
|
||||||
|
/// The return value models public sandbox API values found at:
|
||||||
|
/// https://groups.google.com/forum/#!topic/feedly-cloud/WwQWMgDmOuw
|
||||||
|
/// They are due to expire on November 30 2019.
|
||||||
|
return OAuthAuthorizationClient(id: "sandbox",
|
||||||
|
redirectUri: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
state: nil,
|
||||||
|
secret: "ReVGXA6WekanCxbf")
|
||||||
|
}
|
||||||
|
}
|
@ -61,7 +61,7 @@ public struct OAuthAuthorizationResponse {
|
|||||||
public extension OAuthAuthorizationResponse {
|
public extension OAuthAuthorizationResponse {
|
||||||
|
|
||||||
init(url: URL, client: OAuthAuthorizationClient) throws {
|
init(url: URL, client: OAuthAuthorizationClient) throws {
|
||||||
guard let host = url.host, client.redirectUri.contains(host) else {
|
guard let scheme = url.scheme, client.redirectUri.hasPrefix(scheme) else {
|
||||||
throw URLError(.unsupportedURL)
|
throw URLError(.unsupportedURL)
|
||||||
}
|
}
|
||||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||||
@ -166,9 +166,7 @@ public protocol OAuthAuthorizationCodeGrantRequesting {
|
|||||||
|
|
||||||
protocol OAuthAuthorizationGranting: AccountDelegate {
|
protocol OAuthAuthorizationGranting: AccountDelegate {
|
||||||
|
|
||||||
static var oauthAuthorizationClient: OAuthAuthorizationClient { get }
|
static func oauthAuthorizationCodeGrantRequest() -> URLRequest
|
||||||
|
|
||||||
static func oauthAuthorizationCodeGrantRequest(for client: OAuthAuthorizationClient) -> URLRequest
|
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, completionHandler: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ())
|
||||||
|
|
||||||
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, client: OAuthAuthorizationClient, transport: Transport, completionHandler: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ())
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ PROVISIONING_PROFILE_SPECIFIER =
|
|||||||
#include? "../../../SharedXcodeSettings/DeveloperSettings.xcconfig"
|
#include? "../../../SharedXcodeSettings/DeveloperSettings.xcconfig"
|
||||||
|
|
||||||
SDKROOT = macosx
|
SDKROOT = macosx
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14
|
MACOSX_DEPLOYMENT_TARGET = 10.15
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0
|
||||||
SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator
|
SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator
|
||||||
|
|
||||||
@ -18,7 +18,6 @@ SWIFT_VERSION = 5.1
|
|||||||
COMBINE_HIDPI_IMAGES = YES
|
COMBINE_HIDPI_IMAGES = YES
|
||||||
|
|
||||||
COPY_PHASE_STRIP = NO
|
COPY_PHASE_STRIP = NO
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO
|
ALWAYS_SEARCH_USER_PATHS = NO
|
||||||
CURRENT_PROJECT_VERSION = 1
|
CURRENT_PROJECT_VERSION = 1
|
||||||
VERSION_INFO_PREFIX =
|
VERSION_INFO_PREFIX =
|
||||||
|
@ -101,9 +101,10 @@ extension AccountsAddViewController: NSTableViewDelegate {
|
|||||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||||
accountsAddWindowController = accountsReaderAPIWindowController
|
accountsAddWindowController = accountsReaderAPIWindowController
|
||||||
case .feedly:
|
case .feedly:
|
||||||
let accountsFeedlyWindowController = AccountsFeedlyWebWindowController()
|
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
|
||||||
accountsFeedlyWindowController.runSheetOnWindow(self.view.window!)
|
addAccount.delegate = self
|
||||||
accountsAddWindowController = accountsFeedlyWindowController
|
addAccount.presentationAnchor = self.view.window!
|
||||||
|
OperationQueue.main.addOperation(addAccount)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -113,3 +114,12 @@ extension AccountsAddViewController: NSTableViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: OAuthAccountAuthorizationOperationDelegate
|
||||||
|
|
||||||
|
extension AccountsAddViewController: OAuthAccountAuthorizationOperationDelegate {
|
||||||
|
|
||||||
|
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
|
||||||
|
view.window?.presentError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14865.1"/>
|
|
||||||
<plugIn identifier="com.apple.WebKit2IBPlugin" version="14865.1"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="AccountsFeedlyWebWindowController" customModule="NetNewsWire" customModuleProvider="target">
|
|
||||||
<connections>
|
|
||||||
<outlet property="webView" destination="W4c-Xp-rpq" id="l11-5B-8yc"/>
|
|
||||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
|
||||||
<rect key="contentRect" x="196" y="240" width="480" height="708"/>
|
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
|
||||||
<view key="contentView" id="se5-gp-TjO">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="708"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<wkWebView wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="W4c-Xp-rpq">
|
|
||||||
<rect key="frame" x="0.0" y="61" width="480" height="647"/>
|
|
||||||
<wkWebViewConfiguration key="configuration">
|
|
||||||
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
|
|
||||||
<wkPreferences key="preferences"/>
|
|
||||||
</wkWebViewConfiguration>
|
|
||||||
<connections>
|
|
||||||
<outlet property="navigationDelegate" destination="-2" id="wAp-Oh-5EK"/>
|
|
||||||
</connections>
|
|
||||||
</wkWebView>
|
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PD2-Zk-3yM">
|
|
||||||
<rect key="frame" x="384" y="13" width="82" height="32"/>
|
|
||||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IEi-N0-sbw">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
|
||||||
Gw
|
|
||||||
</string>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="cancel:" target="-2" id="5BT-to-e4W"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="W4c-Xp-rpq" secondAttribute="trailing" id="D9j-IU-BZj"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="PD2-Zk-3yM" secondAttribute="bottom" constant="20" symbolic="YES" id="Qdc-tu-9kO"/>
|
|
||||||
<constraint firstItem="W4c-Xp-rpq" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" id="V7Q-kM-JDA"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="PD2-Zk-3yM" secondAttribute="trailing" constant="20" symbolic="YES" id="bQS-L4-jbx"/>
|
|
||||||
<constraint firstItem="W4c-Xp-rpq" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" id="ec6-U0-t8X"/>
|
|
||||||
<constraint firstItem="PD2-Zk-3yM" firstAttribute="top" secondItem="W4c-Xp-rpq" secondAttribute="bottom" constant="20" symbolic="YES" id="zlA-8I-aKr"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
|
||||||
</connections>
|
|
||||||
<point key="canvasLocation" x="134" y="556"/>
|
|
||||||
</window>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
@ -1,100 +0,0 @@
|
|||||||
//
|
|
||||||
// AccountsFeedlyWebWindowController.swift
|
|
||||||
// NetNewsWire
|
|
||||||
//
|
|
||||||
// Created by Kiel Gillard on 30/8/19.
|
|
||||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import Account
|
|
||||||
import WebKit
|
|
||||||
|
|
||||||
class AccountsFeedlyWebWindowController: NSWindowController, WKNavigationDelegate {
|
|
||||||
|
|
||||||
@IBOutlet private weak var webView: WKWebView!
|
|
||||||
|
|
||||||
private weak var hostWindow: NSWindow?
|
|
||||||
|
|
||||||
convenience init() {
|
|
||||||
self.init(windowNibName: NSNib.Name("AccountsFeedlyWeb"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: API
|
|
||||||
|
|
||||||
func runSheetOnWindow(_ hostWindow: NSWindow, completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil) {
|
|
||||||
self.hostWindow = hostWindow
|
|
||||||
hostWindow.beginSheet(window!, completionHandler: handler)
|
|
||||||
beginAuthorization()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Requesting an Access Token
|
|
||||||
let client = Account.oauthAuthorizationClient(for: .feedly)
|
|
||||||
|
|
||||||
private func beginAuthorization() {
|
|
||||||
let request = Account.oauthAuthorizationCodeGrantRequest(for: .feedly, client: client)
|
|
||||||
webView.load(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func requestAccessToken(for response: OAuthAuthorizationResponse) {
|
|
||||||
Account.requestOAuthAccessToken(with: response, client: client, accountType: .feedly) { [weak self] result in
|
|
||||||
switch result {
|
|
||||||
case .success(let tokenResponse):
|
|
||||||
self?.saveAccount(for: tokenResponse)
|
|
||||||
case .failure(let error):
|
|
||||||
NSApplication.shared.presentError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Actions
|
|
||||||
|
|
||||||
@IBAction func cancel(_ sender: Any) {
|
|
||||||
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
|
||||||
|
|
||||||
do {
|
|
||||||
guard let url = navigationAction.request.url else { return }
|
|
||||||
|
|
||||||
let response = try OAuthAuthorizationResponse(url: url, client: client)
|
|
||||||
|
|
||||||
requestAccessToken(for: response)
|
|
||||||
|
|
||||||
// No point the web view trying to load this.
|
|
||||||
return decisionHandler(.cancel)
|
|
||||||
|
|
||||||
} catch let error as OAuthAuthorizationErrorResponse {
|
|
||||||
NSApplication.shared.presentError(error)
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
decisionHandler(.allow)
|
|
||||||
}
|
|
||||||
|
|
||||||
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveAccount(for grant: OAuthAuthorizationGrant) {
|
|
||||||
// TODO: Find an already existing account for this username?
|
|
||||||
let account = AccountManager.shared.createAccount(type: .feedly)
|
|
||||||
do {
|
|
||||||
|
|
||||||
// Store the refresh token first because it sends this token to the account delegate.
|
|
||||||
if let token = grant.refreshToken {
|
|
||||||
try account.storeCredentials(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now store the access token because we want the account delegate to use it.
|
|
||||||
try account.storeCredentials(grant.accessToken)
|
|
||||||
|
|
||||||
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
|
|
||||||
} catch {
|
|
||||||
NSApplication.shared.presentError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -276,7 +276,6 @@
|
|||||||
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
|
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
|
||||||
65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */; };
|
65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CC88171FE59CBF00644329 /* SmartFeedsController.swift */; };
|
||||||
65ED3FCB235DEF6C0081F399 /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97621ED9EB96007D329B /* SidebarViewController.swift */; };
|
65ED3FCB235DEF6C0081F399 /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97621ED9EB96007D329B /* SidebarViewController.swift */; };
|
||||||
65ED3FCC235DEF6C0081F399 /* AccountsFeedlyWebWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */; };
|
|
||||||
65ED3FCD235DEF6C0081F399 /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97601ED9EB96007D329B /* SidebarOutlineView.swift */; };
|
65ED3FCD235DEF6C0081F399 /* SidebarOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97601ED9EB96007D329B /* SidebarOutlineView.swift */; };
|
||||||
65ED3FCE235DEF6C0081F399 /* DetailKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */; };
|
65ED3FCE235DEF6C0081F399 /* DetailKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */; };
|
||||||
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */; };
|
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */; };
|
||||||
@ -307,7 +306,6 @@
|
|||||||
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||||
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
||||||
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */; };
|
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */; };
|
||||||
65ED3FEB235DEF6C0081F399 /* (null) in Sources */ = {isa = PBXBuildFile; };
|
|
||||||
65ED3FEC235DEF6C0081F399 /* RSHTMLMetadata+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842611A11FCB769D0086A189 /* RSHTMLMetadata+Extension.swift */; };
|
65ED3FEC235DEF6C0081F399 /* RSHTMLMetadata+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842611A11FCB769D0086A189 /* RSHTMLMetadata+Extension.swift */; };
|
||||||
65ED3FED235DEF6C0081F399 /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */; };
|
65ED3FED235DEF6C0081F399 /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */; };
|
||||||
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
||||||
@ -407,7 +405,6 @@
|
|||||||
65ED4050235DEF6C0081F399 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
|
65ED4050235DEF6C0081F399 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
|
||||||
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */; };
|
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */; };
|
||||||
65ED4052235DEF6C0081F399 /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 848362FE2262A30E00DA1D35 /* template.html */; };
|
65ED4052235DEF6C0081F399 /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 848362FE2262A30E00DA1D35 /* template.html */; };
|
||||||
65ED4053235DEF6C0081F399 /* AccountsFeedlyWeb.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */; };
|
|
||||||
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 848363062262A3DD00DA1D35 /* Main.storyboard */; };
|
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 848363062262A3DD00DA1D35 /* Main.storyboard */; };
|
||||||
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51EF0F8D2279C9260050506E /* AccountsAdd.xib */; };
|
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51EF0F8D2279C9260050506E /* AccountsAdd.xib */; };
|
||||||
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 84C9FC8A22629E8F00D921D6 /* NetNewsWire.sdef */; };
|
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 84C9FC8A22629E8F00D921D6 /* NetNewsWire.sdef */; };
|
||||||
@ -615,8 +612,6 @@
|
|||||||
84F9EAF4213660A100CF2DE4 /* testGenericScript.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE1213660A100CF2DE4 /* testGenericScript.applescript */; };
|
84F9EAF4213660A100CF2DE4 /* testGenericScript.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE1213660A100CF2DE4 /* testGenericScript.applescript */; };
|
||||||
84F9EAF5213660A100CF2DE4 /* establishMainWindowStartingState.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */; };
|
84F9EAF5213660A100CF2DE4 /* establishMainWindowStartingState.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */; };
|
||||||
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
||||||
9EA33BB92318F8C10097B644 /* AccountsFeedlyWebWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */; };
|
|
||||||
9EA33BBA2318F8C10097B644 /* AccountsFeedlyWeb.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */; };
|
|
||||||
B528F81E23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; };
|
B528F81E23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; };
|
||||||
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; };
|
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; };
|
||||||
D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; };
|
D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; };
|
||||||
@ -1538,8 +1533,6 @@
|
|||||||
84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = establishMainWindowStartingState.applescript; sourceTree = "<group>"; };
|
84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = establishMainWindowStartingState.applescript; sourceTree = "<group>"; };
|
||||||
84F9EAE4213660A100CF2DE4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
84F9EAE4213660A100CF2DE4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = "<group>"; };
|
84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = "<group>"; };
|
||||||
9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsFeedlyWebWindowController.swift; sourceTree = "<group>"; };
|
|
||||||
9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsFeedlyWeb.xib; sourceTree = "<group>"; };
|
|
||||||
B24EFD482330FF99006C6242 /* NetNewsWire-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-Bridging-Header.h"; sourceTree = "<group>"; };
|
B24EFD482330FF99006C6242 /* NetNewsWire-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = "<group>"; };
|
B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = "<group>"; };
|
||||||
B528F81D23333C7E00E735DD /* page.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = page.html; sourceTree = "<group>"; };
|
B528F81D23333C7E00E735DD /* page.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = page.html; sourceTree = "<group>"; };
|
||||||
@ -2510,8 +2503,6 @@
|
|||||||
5144EA2E2279FAB600D19003 /* AccountsDetailViewController.swift */,
|
5144EA2E2279FAB600D19003 /* AccountsDetailViewController.swift */,
|
||||||
5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */,
|
5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */,
|
||||||
5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */,
|
5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */,
|
||||||
9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */,
|
|
||||||
9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */,
|
|
||||||
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */,
|
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */,
|
||||||
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */,
|
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */,
|
||||||
5144EA352279FC3D00D19003 /* AccountsAddLocal.xib */,
|
5144EA352279FC3D00D19003 /* AccountsAddLocal.xib */,
|
||||||
@ -3359,7 +3350,6 @@
|
|||||||
65ED4050235DEF6C0081F399 /* DetailKeyboardShortcuts.plist in Resources */,
|
65ED4050235DEF6C0081F399 /* DetailKeyboardShortcuts.plist in Resources */,
|
||||||
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */,
|
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||||
65ED4052235DEF6C0081F399 /* template.html in Resources */,
|
65ED4052235DEF6C0081F399 /* template.html in Resources */,
|
||||||
65ED4053235DEF6C0081F399 /* AccountsFeedlyWeb.xib in Resources */,
|
|
||||||
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */,
|
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */,
|
||||||
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */,
|
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */,
|
||||||
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */,
|
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */,
|
||||||
@ -3445,7 +3435,6 @@
|
|||||||
5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */,
|
5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */,
|
||||||
845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */,
|
845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||||
848362FF2262A30E00DA1D35 /* template.html in Resources */,
|
848362FF2262A30E00DA1D35 /* template.html in Resources */,
|
||||||
9EA33BBA2318F8C10097B644 /* AccountsFeedlyWeb.xib in Resources */,
|
|
||||||
848363082262A3DD00DA1D35 /* Main.storyboard in Resources */,
|
848363082262A3DD00DA1D35 /* Main.storyboard in Resources */,
|
||||||
51EF0F8E2279C9260050506E /* AccountsAdd.xib in Resources */,
|
51EF0F8E2279C9260050506E /* AccountsAdd.xib in Resources */,
|
||||||
84C9FC8F22629E8F00D921D6 /* NetNewsWire.sdef in Resources */,
|
84C9FC8F22629E8F00D921D6 /* NetNewsWire.sdef in Resources */,
|
||||||
@ -3762,7 +3751,6 @@
|
|||||||
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */,
|
65ED3FC9235DEF6C0081F399 /* SmartFeedPasteboardWriter.swift in Sources */,
|
||||||
65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */,
|
65ED3FCA235DEF6C0081F399 /* SmartFeedsController.swift in Sources */,
|
||||||
65ED3FCB235DEF6C0081F399 /* SidebarViewController.swift in Sources */,
|
65ED3FCB235DEF6C0081F399 /* SidebarViewController.swift in Sources */,
|
||||||
65ED3FCC235DEF6C0081F399 /* AccountsFeedlyWebWindowController.swift in Sources */,
|
|
||||||
65ED3FCD235DEF6C0081F399 /* SidebarOutlineView.swift in Sources */,
|
65ED3FCD235DEF6C0081F399 /* SidebarOutlineView.swift in Sources */,
|
||||||
65ED3FCE235DEF6C0081F399 /* DetailKeyboardDelegate.swift in Sources */,
|
65ED3FCE235DEF6C0081F399 /* DetailKeyboardDelegate.swift in Sources */,
|
||||||
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */,
|
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */,
|
||||||
@ -3793,7 +3781,6 @@
|
|||||||
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */,
|
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */,
|
||||||
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */,
|
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */,
|
||||||
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */,
|
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */,
|
||||||
65ED3FEB235DEF6C0081F399 /* (null) in Sources */,
|
|
||||||
65ED3FEC235DEF6C0081F399 /* RSHTMLMetadata+Extension.swift in Sources */,
|
65ED3FEC235DEF6C0081F399 /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||||
65ED3FED235DEF6C0081F399 /* SendToMarsEditCommand.swift in Sources */,
|
65ED3FED235DEF6C0081F399 /* SendToMarsEditCommand.swift in Sources */,
|
||||||
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */,
|
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */,
|
||||||
@ -4053,7 +4040,6 @@
|
|||||||
849C78922362AB04009A71E4 /* ExportOPMLWindowController.swift in Sources */,
|
849C78922362AB04009A71E4 /* ExportOPMLWindowController.swift in Sources */,
|
||||||
84CC88181FE59CBF00644329 /* SmartFeedsController.swift in Sources */,
|
84CC88181FE59CBF00644329 /* SmartFeedsController.swift in Sources */,
|
||||||
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */,
|
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */,
|
||||||
9EA33BB92318F8C10097B644 /* AccountsFeedlyWebWindowController.swift in Sources */,
|
|
||||||
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */,
|
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */,
|
||||||
5127B238222B4849006D641D /* DetailKeyboardDelegate.swift in Sources */,
|
5127B238222B4849006D641D /* DetailKeyboardDelegate.swift in Sources */,
|
||||||
8405DD9922153B6B008CE1BF /* TimelineContainerView.swift in Sources */,
|
8405DD9922153B6B008CE1BF /* TimelineContainerView.swift in Sources */,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user