diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 0818d6837..5e0e642b1 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -455,6 +455,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, return ensureFolder(with: folderName) } + public func findFolder(withDisplayName displayName: String) -> Folder? { + return folders?.first(where: { $0.nameForDisplay == displayName }) + } + func newFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> Feed { let feedURL = opmlFeedSpecifier.feedURL let metadata = feedMetadata(feedURL: feedURL, feedID: feedURL) diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index 29b7e9095..7c7ab7f1b 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -58,6 +58,10 @@ public final class AccountManager: UnreadCountProvider { return sortByName(activeAccounts) } + public func findActiveAccount(forDisplayName displayName: String) -> Account? { + return AccountManager.shared.activeAccounts.first(where: { $0.nameForDisplay == displayName }) + } + public var refreshInProgress: Bool { for account in activeAccounts { if account.refreshInProgress { diff --git a/iOS/Intents/AddFeedIntentHandler.swift b/iOS/Intents/AddFeedIntentHandler.swift index 36bacf429..ab822fd16 100644 --- a/iOS/Intents/AddFeedIntentHandler.swift +++ b/iOS/Intents/AddFeedIntentHandler.swift @@ -10,7 +10,7 @@ import Intents import Account public class AddFeedIntentHandler: NSObject, AddFeedIntentHandling { - + override init() { super.init() DispatchQueue.main.sync { @@ -35,25 +35,85 @@ public class AddFeedIntentHandler: NSObject, AddFeedIntentHandling { public func resolveAccountName(for intent: AddFeedIntent, with completion: @escaping (AddFeedAccountNameResolutionResult) -> Void) { guard let accountName = intent.accountName else { - completion(.unsupported(forReason: .required)) + completion(AddFeedAccountNameResolutionResult.notRequired()) + return + } + DispatchQueue.main.async { + if AccountManager.shared.findActiveAccount(forDisplayName: accountName) == nil { + completion(.unsupported(forReason: .invalid)) + } else { + completion(.success(with: accountName)) + } + } + } + + public func provideFolderNameOptions(for intent: AddFeedIntent, with completion: @escaping ([String]?, Error?) -> Void) { + DispatchQueue.main.async { + guard let accountName = intent.accountName, let account = AccountManager.shared.findActiveAccount(forDisplayName: accountName) else { + completion([String](), nil) + return + } + + let folderNames = account.folders?.map { $0.nameForDisplay } + completion(folderNames, nil) + } + } + + public func resolveFolderName(for intent: AddFeedIntent, with completion: @escaping (AddFeedFolderNameResolutionResult) -> Void) { + guard let accountName = intent.accountName, let folderName = intent.folderName else { + completion(AddFeedFolderNameResolutionResult.notRequired()) + return + } + + DispatchQueue.main.async { + guard let account = AccountManager.shared.findActiveAccount(forDisplayName: accountName) else { + completion(.unsupported(forReason: .invalid)) + return + } + if account.findFolder(withDisplayName: folderName) == nil { + completion(.unsupported(forReason: .invalid)) + } else { + completion(.success(with: folderName)) + } return } - completion(.success(with: accountName)) } public func handle(intent: AddFeedIntent, completion: @escaping (AddFeedIntentResponse) -> Void) { - guard let url = intent.url, let accountName = intent.accountName else { + guard let url = intent.url else { completion(AddFeedIntentResponse(code: .failure, userActivity: nil)) return } DispatchQueue.main.async { - guard let account = AccountManager.shared.activeAccounts.first(where: { $0.nameForDisplay == accountName }) else { + + let account: Account? = { + if let accountName = intent.accountName { + return AccountManager.shared.findActiveAccount(forDisplayName: accountName) + } else { + return AccountManager.shared.sortedActiveAccounts.first + } + }() + + guard let validAccount = account else { completion(AddFeedIntentResponse(code: .failure, userActivity: nil)) return } - account.createFeed(url: url.absoluteString, name: nil, container: account) { result in + let container: Container? = { + if let folderName = intent.folderName { + return validAccount.findFolder(withDisplayName: folderName) + } else { + return validAccount + } + }() + + guard let validContainer = container else { + completion(AddFeedIntentResponse(code: .failure, userActivity: nil)) + return + } + + validAccount.createFeed(url: url.absoluteString, name: nil, container: validContainer) { result in switch result { case .success: completion(AddFeedIntentResponse(code: .success, userActivity: nil)) diff --git a/iOS/Intents/Base.lproj/Intents.intentdefinition b/iOS/Intents/Base.lproj/Intents.intentdefinition index a3ce6dc74..7994aa105 100644 --- a/iOS/Intents/Base.lproj/Intents.intentdefinition +++ b/iOS/Intents/Base.lproj/Intents.intentdefinition @@ -32,7 +32,7 @@ INIntentKeyParameter url INIntentLastParameterTag - 3 + 4 INIntentManagedParameterCombinations url,accountName @@ -40,7 +40,18 @@ INIntentParameterCombinationSupportsBackgroundExecution INIntentParameterCombinationTitle - Add${url}to ${accountName} + Add ${url} to ${accountName} + INIntentParameterCombinationTitleID + kaKsEY + INIntentParameterCombinationUpdatesLinked + + + url,accountName,folderName + + INIntentParameterCombinationSupportsBackgroundExecution + + INIntentParameterCombinationTitle + Add ${url} to ${folderName} in ${accountName} INIntentParameterCombinationTitleID dkSFD2 INIntentParameterCombinationUpdatesLinked @@ -66,7 +77,7 @@ INIntentParameterPromptDialogCustom INIntentParameterPromptDialogFormatString - What is the ${url}you would like add? + What is the ${url} you would like add? INIntentParameterPromptDialogFormatStringID jLLidQ INIntentParameterPromptDialogType @@ -158,16 +169,98 @@ INIntentParameterUnsupportedReasonCode - required + invalid INIntentParameterUnsupportedReasonCustom INIntentParameterUnsupportedReasonFormatString - An account name is required. + A valid Account Name is required. INIntentParameterUnsupportedReasonFormatStringID JGkCuS + + INIntentParameterCustomDisambiguation + + INIntentParameterDisplayName + Folder Name + INIntentParameterDisplayNameID + zXhMPF + INIntentParameterDisplayPriority + 3 + INIntentParameterMetadata + + INIntentParameterMetadataCapitalization + Sentences + + INIntentParameterName + folderName + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Primary + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + There are ${count} options matching ‘${folderName}’. + INIntentParameterPromptDialogFormatStringID + 5CYbGL + INIntentParameterPromptDialogType + DisambiguationIntroduction + + + INIntentParameterPromptDialogFormatString + Which one? + INIntentParameterPromptDialogFormatStringID + gEzXaM + INIntentParameterPromptDialogType + DisambiguationSelection + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Just to confirm, you wanted ‘${folderName}’? + INIntentParameterPromptDialogFormatStringID + k5GTo0 + INIntentParameterPromptDialogType + Confirmation + + + INIntentParameterRelationship + + INIntentParameterRelationshipParentName + accountName + INIntentParameterRelationshipPredicateName + HasAnyValue + + INIntentParameterSupportsDynamicEnumeration + + INIntentParameterSupportsResolution + + INIntentParameterTag + 4 + INIntentParameterType + String + INIntentParameterUnsupportedReasons + + + INIntentParameterUnsupportedReasonCode + invalid + INIntentParameterUnsupportedReasonCustom + + INIntentParameterUnsupportedReasonFormatString + A valid Folder Name is required. + INIntentParameterUnsupportedReasonFormatStringID + ef5kBt + + + INIntentResponse