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