diff --git a/IntentsExtension/Extensions/IntentNote+Helpers.swift b/IntentsExtension/Extensions/IntentNote+Helpers.swift index b645446b1..06a2d69f7 100644 --- a/IntentsExtension/Extensions/IntentNote+Helpers.swift +++ b/IntentsExtension/Extensions/IntentNote+Helpers.swift @@ -8,6 +8,44 @@ import Intents +extension IntentNoteResolutionResult { + static func resolve(_ intentNote: IntentNote?, in coreDataWrapper: ExtensionCoreDataWrapper) -> IntentNoteResolutionResult { + guard let intentNote = intentNote, + let identifier = intentNote.identifier else { + return IntentNoteResolutionResult.needsValue() + } + + guard coreDataWrapper.resultsController()?.noteExists(forSimperiumKey: identifier) == true else { + return IntentNoteResolutionResult.unsupported() + } + + return IntentNoteResolutionResult.success(with: intentNote) + } + + static func resolveIntentNote(for content: String, in coreDataWrapper: ExtensionCoreDataWrapper) -> IntentNoteResolutionResult { + guard let notes = coreDataWrapper.resultsController()?.notes() else { + return IntentNoteResolutionResult.unsupported() + } + let filteredNotes = notes.filter({ $0.content?.contains(content) == true }) + let intentNotes = IntentNote.makeIntentNotes(from: filteredNotes) + + return resolve(intentNotes) + } + + private static func resolve(_ intentNotes: [IntentNote]) -> IntentNoteResolutionResult { + guard intentNotes.isEmpty == false else { + return IntentNoteResolutionResult.unsupported() + } + + if intentNotes.count == 1, + let intentNote = intentNotes.first { + return IntentNoteResolutionResult.success(with: intentNote) + } + + return IntentNoteResolutionResult.disambiguation(with: intentNotes) + } +} + extension IntentNote { static func allNotes(in coreDataWrapper: ExtensionCoreDataWrapper) throws -> [IntentNote] { guard let notes = coreDataWrapper.resultsController()?.notes() else { diff --git a/IntentsExtension/IntentHandler.swift b/IntentsExtension/IntentHandler.swift index 38a70a374..0888415cb 100644 --- a/IntentsExtension/IntentHandler.swift +++ b/IntentsExtension/IntentHandler.swift @@ -15,6 +15,8 @@ class IntentHandler: INExtension { return OpenNewNoteIntentHandler() case is OpenNoteIntent: return OpenNoteIntentHandler() + case is FindNoteIntent: + return FindNoteIntentHandler() default: return self } diff --git a/IntentsExtension/IntentHandlers/FindNoteIntentHandler.swift b/IntentsExtension/IntentHandlers/FindNoteIntentHandler.swift new file mode 100644 index 000000000..d157bb5a3 --- /dev/null +++ b/IntentsExtension/IntentHandlers/FindNoteIntentHandler.swift @@ -0,0 +1,49 @@ +// +// FindNoteIntentHandler.swift +// IntentsExtension +// +// Created by Charlie Scheer on 5/29/24. +// Copyright © 2024 Simperium. All rights reserved. +// + +import Intents + +class FindNoteIntentHandler: NSObject, FindNoteIntentHandling { + let coreDataWrapper = ExtensionCoreDataWrapper() + + func resolveNote(for intent: FindNoteIntent, with completion: @escaping (IntentNoteResolutionResult) -> Void) { + // If the user has already selected a note return that note with success + if let selectedNote = intent.note { + completion(IntentNoteResolutionResult.success(with: selectedNote)) + return + } + + guard let content = intent.content else { + completion(IntentNoteResolutionResult.needsValue()) + return + } + + completion(IntentNoteResolutionResult.resolveIntentNote(for: content, in: coreDataWrapper)) + } + + func provideNoteOptionsCollection(for intent: FindNoteIntent, with completion: @escaping (INObjectCollection?, (any Error)?) -> Void) { + do { + let intentNotes = try IntentNote.allNotes(in: coreDataWrapper) + completion(INObjectCollection(items: intentNotes), nil) + } catch { + completion(nil, IntentsError.couldNotFetchNotes) + } + } + + func handle(intent: FindNoteIntent, completion: @escaping (FindNoteIntentResponse) -> Void) { + guard let intentNote = intent.note else { + completion(FindNoteIntentResponse(code: .failure, userActivity: nil)) + return + } + + let success = FindNoteIntentResponse(code: .success, userActivity: nil) + success.note = intentNote + + completion(success) + } +} diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index 060a8b8a2..59b70bf02 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -252,6 +252,7 @@ BA0B43CA26F2FCFC00B44A8C /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA0B43C826F2FCFC00B44A8C /* PreferencesViewController.swift */; }; BA0B43D626F2FEA200B44A8C /* Preferences.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BA0B43D426F2FEA200B44A8C /* Preferences.storyboard */; }; BA188D2E2C01117200787855 /* Pods_Automattic_Simplenote.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0F7AA366214AF89DB7A1C687 /* Pods_Automattic_Simplenote.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BA2BF3402C07C75500A7C894 /* FindNoteIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2BF33F2C07C75500A7C894 /* FindNoteIntentHandler.swift */; }; BA2C65CF26FE996A00FA84E1 /* NSButton+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2C65CA26FE996100FA84E1 /* NSButton+Extensions.swift */; }; BA4C6D16264CA8C000B723A7 /* SignupRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4C6D15264CA8C000B723A7 /* SignupRemoteTests.swift */; }; BA4C6D18264CAAF800B723A7 /* URLRequest+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4C6D17264CAAF800B723A7 /* URLRequest+Simplenote.swift */; }; @@ -678,6 +679,7 @@ B5F807CE2481A2010048CBD7 /* Simperium+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Simperium+Simplenote.swift"; sourceTree = ""; }; BA0B43C826F2FCFC00B44A8C /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; BA0B43D426F2FEA200B44A8C /* Preferences.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Preferences.storyboard; sourceTree = ""; }; + BA2BF33F2C07C75500A7C894 /* FindNoteIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNoteIntentHandler.swift; sourceTree = ""; }; BA2C65CA26FE996100FA84E1 /* NSButton+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSButton+Extensions.swift"; sourceTree = ""; }; BA39C4542C0133F30004B2A9 /* SimplenoteDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SimplenoteDebug.entitlements; sourceTree = ""; }; BA39C4552C0134180004B2A9 /* IntentsExtensionDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtensionDebug.entitlements; sourceTree = ""; }; @@ -1585,6 +1587,7 @@ children = ( BAB261742BFFD319009A98D7 /* OpenNewNoteIntentHandler.swift */, BAC5DFB82C079E5A002AD7EF /* OpenNoteIntentHandler.swift */, + BA2BF33F2C07C75500A7C894 /* FindNoteIntentHandler.swift */, ); path = IntentHandlers; sourceTree = ""; @@ -2167,6 +2170,7 @@ BAAA71E32C07A2C200244C01 /* SortMode.swift in Sources */, BAAA71E42C07A2F900244C01 /* NSUserInterfaceItemIdentifier+Simplenote.swift in Sources */, BAB261762BFFD319009A98D7 /* OpenNewNoteIntentHandler.swift in Sources */, + BA2BF3402C07C75500A7C894 /* FindNoteIntentHandler.swift in Sources */, BAAA71D72C07A11500244C01 /* Note+Widget.swift in Sources */, BAAA71D82C07A12F00244C01 /* NoteContentHelper.swift in Sources */, BAAA71EA2C07A58100244C01 /* Simplenote.xcdatamodeld in Sources */, diff --git a/Simplenote/ShortcutIntents.intentdefinition b/Simplenote/ShortcutIntents.intentdefinition index 753954515..7a74d72e9 100644 --- a/Simplenote/ShortcutIntents.intentdefinition +++ b/Simplenote/ShortcutIntents.intentdefinition @@ -290,6 +290,31 @@ failure + INIntentResponseLastParameterTag + 2 + INIntentResponseOutput + note + INIntentResponseParameters + + + INIntentResponseParameterDisplayName + Note + INIntentResponseParameterDisplayNameID + HIbqhe + INIntentResponseParameterDisplayPriority + 1 + INIntentResponseParameterName + note + INIntentResponseParameterObjectType + IntentNote + INIntentResponseParameterObjectTypeNamespace + ihfyvu + INIntentResponseParameterTag + 2 + INIntentResponseParameterType + Object + + INIntentTitle Find Note With Content