From 078f4c883a13fdce978ba201e3dce02d7c054155 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 14:22:30 -0600 Subject: [PATCH 01/18] Added CSSearchable helpers to easily index notes from SN --- Simplenote.xcodeproj/project.pbxproj | 4 ++ Simplenote/CSSearchable+Helpers.swift | 72 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Simplenote/CSSearchable+Helpers.swift diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index aae115ef7..838f41f73 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -462,6 +462,7 @@ 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 */; }; + BA52005B2BC878F1003F1B75 /* CSSearchable+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */; }; BA553F0827065E20007737E9 /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA553F0727065E20007737E9 /* FontSettings.swift */; }; BA553F0927065E20007737E9 /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA553F0727065E20007737E9 /* FontSettings.swift */; }; BA5F020526BB57F000581E92 /* NSAlert+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */; }; @@ -868,6 +869,7 @@ BA2C65CA26FE996100FA84E1 /* NSButton+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSButton+Extensions.swift"; sourceTree = ""; }; BA4C6D15264CA8C000B723A7 /* SignupRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupRemoteTests.swift; sourceTree = ""; }; BA4C6D17264CAAF800B723A7 /* URLRequest+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Simplenote.swift"; sourceTree = ""; }; + BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSSearchable+Helpers.swift"; sourceTree = ""; }; BA553F0727065E20007737E9 /* FontSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSettings.swift; sourceTree = ""; }; BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+Simplenote.swift"; sourceTree = ""; }; BA938CEB26ACFF4A00BE5A1D /* Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Remote.swift; sourceTree = ""; }; @@ -1251,6 +1253,7 @@ children = ( B5D21CB624881EF600D57A34 /* Array+Simplenote.swift */, B57CB87D244DED2300BA7969 /* Bundle+Simplenote.swift */, + BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */, B53BF19B24ABDE7C00938C34 /* DateFormatter+Simplenote.swift */, B5C620AA257ED4CF008359A9 /* NSAnimationContext+Simplenote.swift */, B56FA79A2437D2E0002CB9FF /* NSAppearance+Simplenote.swift */, @@ -2120,6 +2123,7 @@ BA2C65CB26FE996100FA84E1 /* NSButton+Extensions.swift in Sources */, B58117E225B9E5D200927E0C /* AccountVerificationController.swift in Sources */, B5009937242130F70037A431 /* UnicodeScalar+Simplenote.swift in Sources */, + BA52005B2BC878F1003F1B75 /* CSSearchable+Helpers.swift in Sources */, B5985AD5242950B40044EDE9 /* NSColor+Simplenote.swift in Sources */, B5C7DD3D243E1F1900BEE354 /* VersionsViewController.swift in Sources */, B58BBD3D2523FF160025135F /* PopoverWindow.swift in Sources */, diff --git a/Simplenote/CSSearchable+Helpers.swift b/Simplenote/CSSearchable+Helpers.swift new file mode 100644 index 000000000..01c2465cc --- /dev/null +++ b/Simplenote/CSSearchable+Helpers.swift @@ -0,0 +1,72 @@ +// +// CSSearchableItem+Helpers.swift +// Simplenote +// +// Created by Michal Kosmowski on 25/11/2016. +// Copyright © 2016 Automattic. All rights reserved. +// + +import Foundation +import CoreSpotlight +import UniformTypeIdentifiers + +extension CSSearchableItemAttributeSet { + + convenience init(note: Note) { + self.init(contentType: UTType.data) + note.ensurePreviewStringsAreAvailable() + title = note.titlePreview + contentDescription = note.bodyPreview + } + +} + +extension CSSearchableItem { + + convenience init(note: Note) { + let attributeSet = CSSearchableItemAttributeSet(note: note) + self.init(uniqueIdentifier: note.simperiumKey, domainIdentifier: "notes", attributeSet: attributeSet) + } + +} + +extension CSSearchableIndex { + + @objc func indexSearchableNote(_ note: Note) { + let item = CSSearchableItem(note: note) + indexSearchableItems([item]) { error in + if let error = error { + NSLog("Couldn't index note in spotlight: \(error.localizedDescription)") + } + } + } + + @objc func indexSearchableNotes(_ notes: [Note]) { + let items = notes.map { + return CSSearchableItem(note: $0) + } + + indexSearchableItems(items) { error in + if let error = error { + NSLog("Couldn't index notes in spotlight: \(error.localizedDescription)") + } + } + } + + @objc func deleteSearchableNote(_ note: Note) { + deleteSearchableNotes([note]) + } + + @objc func deleteSearchableNotes(_ notes: [Note]) { + let ids = notes.map { + return $0.simperiumKey! + } + + deleteSearchableItems(withIdentifiers: ids) { error in + if let error = error { + NSLog("Couldn't delete notes from spotlight index: \(error.localizedDescription)") + } + } + } + +} From 9132eecf09fbc4604961453f68168a6342568614 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 14:23:00 -0600 Subject: [PATCH 02/18] Added indexing of notes when they are saved or updated by sync --- Simplenote/NoteEditorViewController.m | 2 ++ Simplenote/NoteListViewController.swift | 2 ++ Simplenote/SimplenoteAppDelegate.m | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/Simplenote/NoteEditorViewController.m b/Simplenote/NoteEditorViewController.m index 4c3146e4a..d01363bd6 100644 --- a/Simplenote/NoteEditorViewController.m +++ b/Simplenote/NoteEditorViewController.m @@ -148,6 +148,8 @@ - (void)save [self.saveTimer invalidate]; self.saveTimer = nil; + [[CSSearchableIndex defaultSearchableIndex] indexSearchableNote:self.note]; + if (editorHasFocus) { [[NSApp keyWindow] makeFirstResponder:self.noteEditor]; diff --git a/Simplenote/NoteListViewController.swift b/Simplenote/NoteListViewController.swift index dc24f2cc5..d5e790ba7 100644 --- a/Simplenote/NoteListViewController.swift +++ b/Simplenote/NoteListViewController.swift @@ -1,5 +1,6 @@ import Foundation import SimplenoteSearch +import CoreSpotlight // MARK: - NotesControllerDelegate // @@ -865,6 +866,7 @@ extension NoteListViewController { note.deleted = false simperium.save() + CSSearchableIndex.default().indexSearchableNote(note) SPTracker.trackListNoteRestored() } } diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 50653ec48..3ba8725e6 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -286,6 +286,13 @@ - (void)bucket:(SPBucket *)bucket didChangeObjectForKey:(NSString *)key forChang } [self.noteEditorMetadataCache didUpdateNote:note]; + + if (note && !note.deleted) { + [[CSSearchableIndex defaultSearchableIndex] indexSearchableNote:note]; + } else { + [[CSSearchableIndex defaultSearchableIndex] deleteSearchableItemsWithIdentifiers:@[key] completionHandler:nil]; + } + break; } From 98affe92ff0d974ef4b4c18c65821fd2a071a311 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 14:42:01 -0600 Subject: [PATCH 03/18] Remove spot light index of notes when deleted --- Simplenote/NoteEditorViewController.m | 1 + Simplenote/NoteListViewController.swift | 1 + Simplenote/SimplenoteAppDelegate.m | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Simplenote/NoteEditorViewController.m b/Simplenote/NoteEditorViewController.m index d01363bd6..238a09af4 100644 --- a/Simplenote/NoteEditorViewController.m +++ b/Simplenote/NoteEditorViewController.m @@ -406,6 +406,7 @@ - (IBAction)deleteAction:(id)sender [SPTracker trackEditorNoteDeleted]; noteToDelete.deleted = YES; [self.noteActionsDelegate editorController:self deletedNoteWithSimperiumKey:noteToDelete.simperiumKey]; + [[CSSearchableIndex defaultSearchableIndex] deleteSearchableNote:noteToDelete]; } [self save]; diff --git a/Simplenote/NoteListViewController.swift b/Simplenote/NoteListViewController.swift index d5e790ba7..597b35d75 100644 --- a/Simplenote/NoteListViewController.swift +++ b/Simplenote/NoteListViewController.swift @@ -812,6 +812,7 @@ extension NoteListViewController { for note in selectedNotes { SPTracker.trackListNoteDeleted() note.deleted = true + CSSearchableIndex.default().deleteSearchableNote(note) } simperium.save() diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 3ba8725e6..97c65b3c3 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -251,6 +251,8 @@ - (void)simperiumDidLogout:(Simperium *)simperium [self.crashLogging clearCachedUser]; [self.noteEditorMetadataCache removeAll]; + + [[CSSearchableIndex defaultSearchableIndex] deleteAllSearchableItemsWithCompletionHandler:nil]; } - (void)simperium:(Simperium *)simperium didFailWithError:(NSError *)error From db42b63c6186e7cd6d3c7c3a3ef74082e591d3d7 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 14:42:14 -0600 Subject: [PATCH 04/18] Open notes from spotlight search --- Simplenote/SimplenoteAppDelegate+Swift.swift | 17 +++++++++++++++++ Simplenote/SimplenoteAppDelegate.m | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/Simplenote/SimplenoteAppDelegate+Swift.swift b/Simplenote/SimplenoteAppDelegate+Swift.swift index 0627ebb7d..1230d284b 100644 --- a/Simplenote/SimplenoteAppDelegate+Swift.swift +++ b/Simplenote/SimplenoteAppDelegate+Swift.swift @@ -1,6 +1,7 @@ import Foundation import SimplenoteSearch import Simperium_OSX +import CoreSpotlight // MARK: - Initialization // @@ -237,6 +238,22 @@ extension SimplenoteAppDelegate { SPTracker.trackSettingsStatusBarDisplayMode(hidden: Options.shared.statusBarHidden) } + + @objc + func handleUserActivity(_ userActivity: NSUserActivity) { + if userActivity.activityType == "com.apple.corespotlightitem" { + presentNote(for: userActivity) + return + } + } + + func presentNote(for userActivity: NSUserActivity) { + guard let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { + return + } + + displayNote(simperiumKey: uniqueIdentifier) + } } // MARK: - URL Handlers diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 97c65b3c3..1aa263cc5 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -232,6 +232,10 @@ - (IBAction)aboutAction:(id)sender [self.aboutWindowController showWindow:self]; } +- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nonnull))restorationHandler +{ + [self handleUserActivity:userActivity]; +} #pragma mark - Simperium Delegates From aabd303a1109145fdac4017f02826fe7343c6056 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 14:54:31 -0600 Subject: [PATCH 05/18] One time Index all notes in simplenote on launch --- Simplenote.xcodeproj/project.pbxproj | 4 ++ .../NSManagedObjectContext+Simplenote.swift | 22 ++++++++++ Simplenote/SimplenoteAppDelegate.m | 41 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 Simplenote/NSManagedObjectContext+Simplenote.swift diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index 838f41f73..a92b75158 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -463,6 +463,7 @@ BA4C6D16264CA8C000B723A7 /* SignupRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4C6D15264CA8C000B723A7 /* SignupRemoteTests.swift */; }; BA4C6D18264CAAF800B723A7 /* URLRequest+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4C6D17264CAAF800B723A7 /* URLRequest+Simplenote.swift */; }; BA52005B2BC878F1003F1B75 /* CSSearchable+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */; }; + BA52005D2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA52005C2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift */; }; BA553F0827065E20007737E9 /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA553F0727065E20007737E9 /* FontSettings.swift */; }; BA553F0927065E20007737E9 /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA553F0727065E20007737E9 /* FontSettings.swift */; }; BA5F020526BB57F000581E92 /* NSAlert+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */; }; @@ -870,6 +871,7 @@ BA4C6D15264CA8C000B723A7 /* SignupRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupRemoteTests.swift; sourceTree = ""; }; BA4C6D17264CAAF800B723A7 /* URLRequest+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Simplenote.swift"; sourceTree = ""; }; BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSSearchable+Helpers.swift"; sourceTree = ""; }; + BA52005C2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Simplenote.swift"; sourceTree = ""; }; BA553F0727065E20007737E9 /* FontSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSettings.swift; sourceTree = ""; }; BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAlert+Simplenote.swift"; sourceTree = ""; }; BA938CEB26ACFF4A00BE5A1D /* Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Remote.swift; sourceTree = ""; }; @@ -1300,6 +1302,7 @@ BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */, BAFB544F26CCA7F1006E037C /* NSProgressIndicator+Simplenote.swift */, BA2C65CA26FE996100FA84E1 /* NSButton+Extensions.swift */, + BA52005C2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift */, ); name = Extensions; sourceTree = ""; @@ -2159,6 +2162,7 @@ B59EA98124AA5EFA008ABE4B /* NoteMetrics.swift in Sources */, B5EDF338258A8F1B0066D91D /* TagListFilter.swift in Sources */, 375D293621E033D1007AB25A /* document.c in Sources */, + BA52005D2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift in Sources */, B5609AEC24EEE7200097777A /* SPBucket+Simplenote.swift in Sources */, B56FA7902437C672002CB9FF /* ColorStudio.swift in Sources */, B5177CD025EEEEFB00A8D834 /* NSWindow+Transitions.swift in Sources */, diff --git a/Simplenote/NSManagedObjectContext+Simplenote.swift b/Simplenote/NSManagedObjectContext+Simplenote.swift new file mode 100644 index 000000000..d106b2a5b --- /dev/null +++ b/Simplenote/NSManagedObjectContext+Simplenote.swift @@ -0,0 +1,22 @@ +// +// NSManagedObjectContext+Simplenote.swift +// Simplenote +// +// Created by Charlie Scheer on 4/11/24. +// Copyright © 2024 Simperium. All rights reserved. +// + +import Foundation +import CoreData + +extension NSManagedObjectContext { + @objc(fetchObjectsForEntityName: withPredicate: error:) + func fetchObjects(for entityName: String, withPredicate predicate: NSPredicate) throws -> Array { + let fetchRequest = NSFetchRequest() + let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: self) + + fetchRequest.entity = entityDescription + + return try fetch(fetchRequest) + } +} diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 1aa263cc5..61909dd0b 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -123,6 +123,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification [self cleanupTags]; [self startListeningForThemeNotifications]; + [self indexSpotlightItemsIfNeeded]; + [SPTracker trackApplicationLaunched]; } @@ -570,4 +572,43 @@ - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window return [[self managedObjectContext] undoManager]; } + +#pragma mark ================================================================================ +#pragma mark Spotlight +#pragma mark ================================================================================ + +- (void)indexSpotlightItemsIfNeeded +{ + // This process should be executed *just once*, and only if the user is already logged in (AKA "Upgrade") + NSString *kSpotlightDidRunKey = @"SpotlightDidRunKey"; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + if ([defaults boolForKey:kSpotlightDidRunKey] == true) { + return; + } + + [defaults setBool:true forKey:kSpotlightDidRunKey]; + [defaults synchronize]; + + if (self.simperium.user.authenticated == false) { + return; + } + + [self indexSpotlightItems]; +} + +- (void)indexSpotlightItems +{ + NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + [context setParentContext:self.simperium.managedObjectContext]; + + [context performBlock:^{ + NSArray *deleted = [context fetchObjectsForEntityName:@"Note" withPredicate:[NSPredicate predicateWithFormat:@"deleted == YES"] error:nil]; + [[CSSearchableIndex defaultSearchableIndex] deleteSearchableNotes:deleted]; + + NSArray *notes = [context fetchObjectsForEntityName:@"Note" withPredicate:[NSPredicate predicateWithFormat:@"deleted == NO"] error:nil]; + [[CSSearchableIndex defaultSearchableIndex] indexSearchableNotes:notes]; + }]; +} + @end From 5dad3d31017174572672b4545a70b6f67db2c357 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 15:32:55 -0600 Subject: [PATCH 06/18] Fixed warning of incompatible return type in app delegate --- Simplenote/SimplenoteAppDelegate+Swift.swift | 6 ++++-- Simplenote/SimplenoteAppDelegate.m | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Simplenote/SimplenoteAppDelegate+Swift.swift b/Simplenote/SimplenoteAppDelegate+Swift.swift index 1230d284b..efc242e6a 100644 --- a/Simplenote/SimplenoteAppDelegate+Swift.swift +++ b/Simplenote/SimplenoteAppDelegate+Swift.swift @@ -240,11 +240,13 @@ extension SimplenoteAppDelegate { } @objc - func handleUserActivity(_ userActivity: NSUserActivity) { + func handleUserActivity(_ userActivity: NSUserActivity) -> Bool { if userActivity.activityType == "com.apple.corespotlightitem" { presentNote(for: userActivity) - return + return true } + + return false } func presentNote(for userActivity: NSUserActivity) { diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 61909dd0b..3856aa198 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -236,7 +236,7 @@ - (IBAction)aboutAction:(id)sender - (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nonnull))restorationHandler { - [self handleUserActivity:userActivity]; + return [self handleUserActivity:userActivity]; } #pragma mark - Simperium Delegates From 2e6307c4daba4542f4067f13a02a70c451594c48 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Thu, 11 Apr 2024 15:39:52 -0600 Subject: [PATCH 07/18] Fixed issue with appstore build that was failing the CI --- Simplenote.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Simplenote.xcodeproj/project.pbxproj b/Simplenote.xcodeproj/project.pbxproj index a92b75158..817541725 100644 --- a/Simplenote.xcodeproj/project.pbxproj +++ b/Simplenote.xcodeproj/project.pbxproj @@ -468,6 +468,8 @@ BA553F0927065E20007737E9 /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA553F0727065E20007737E9 /* FontSettings.swift */; }; BA5F020526BB57F000581E92 /* NSAlert+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */; }; BA5F020626BB57F000581E92 /* NSAlert+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA5F020426BB57F000581E92 /* NSAlert+Simplenote.swift */; }; + BA71EC242BC88FD000F42CB1 /* CSSearchable+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA52005A2BC878F1003F1B75 /* CSSearchable+Helpers.swift */; }; + BA71EC252BC88FFC00F42CB1 /* NSManagedObjectContext+Simplenote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA52005C2BC88397003F1B75 /* NSManagedObjectContext+Simplenote.swift */; }; BA78AF6F2B5B2BBA00DCF896 /* AutomatticTracks in Frameworks */ = {isa = PBXBuildFile; productRef = BA78AF6E2B5B2BBA00DCF896 /* AutomatticTracks */; }; BA78AF712B5B2BC300DCF896 /* AutomatticTracks in Frameworks */ = {isa = PBXBuildFile; productRef = BA78AF702B5B2BC300DCF896 /* AutomatticTracks */; }; BA938CEC26ACFF4A00BE5A1D /* Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA938CEB26ACFF4A00BE5A1D /* Remote.swift */; }; @@ -2320,6 +2322,7 @@ BA2C65CF26FE996A00FA84E1 /* NSButton+Extensions.swift in Sources */, 375D294121E033D1007AB25A /* html_smartypants.c in Sources */, B5E061782450AEDA0076111A /* ToolbarView.swift in Sources */, + BA71EC242BC88FD000F42CB1 /* CSSearchable+Helpers.swift in Sources */, B502C1DE25BA2EB700145D6C /* AccountRemote.swift in Sources */, F998F3EC22853C49008C2B59 /* CrashLogging.swift in Sources */, B5919365245A7AD300A70C0C /* NSScreen+Simplenote.swift in Sources */, @@ -2355,6 +2358,7 @@ B5F807CD2481982B0048CBD7 /* Note+Simplenote.swift in Sources */, A6C1E21525E010140076ADF7 /* SPApplication.swift in Sources */, 466FFEB417CC10A800399652 /* DateTransformer.m in Sources */, + BA71EC252BC88FFC00F42CB1 /* NSManagedObjectContext+Simplenote.swift in Sources */, B5132FA923C4B9760065DD80 /* NSTextStorage+Simplenote.swift in Sources */, 375D293721E033D1007AB25A /* document.c in Sources */, 376EE3EC202B748E00E3812E /* SPAboutTextField.swift in Sources */, From 3e9ef6143bed56767132817c2e51b70c90ca5d1a Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Fri, 12 Apr 2024 10:30:18 -0600 Subject: [PATCH 08/18] Updated release notes for PR1140 add spotlight support --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 2416934a4..a6295ff9f 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,6 +1,6 @@ 2.21 ----- - +- Added spotlight search support for notes 2.20 ----- From f14405483a70520745c9153a54a1b49df96ca4fe Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Mon, 15 Apr 2024 12:26:14 -0600 Subject: [PATCH 09/18] user searchable item activity type instead of inline constant --- Simplenote/SimplenoteAppDelegate+Swift.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Simplenote/SimplenoteAppDelegate+Swift.swift b/Simplenote/SimplenoteAppDelegate+Swift.swift index efc242e6a..0ef7a0136 100644 --- a/Simplenote/SimplenoteAppDelegate+Swift.swift +++ b/Simplenote/SimplenoteAppDelegate+Swift.swift @@ -241,7 +241,7 @@ extension SimplenoteAppDelegate { @objc func handleUserActivity(_ userActivity: NSUserActivity) -> Bool { - if userActivity.activityType == "com.apple.corespotlightitem" { + if userActivity.activityType == CSSearchableItemActionType { presentNote(for: userActivity) return true } From 6f7dbef688557509635776b36a53f3cfe7207a77 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Mon, 15 Apr 2024 12:51:25 -0600 Subject: [PATCH 10/18] Added option to enable or disable spotlight indexing --- Simplenote/Options.swift | 13 +++++++++++++ Simplenote/SimplenoteAppDelegate.m | 11 ++++------- Simplenote/UserDefaults+Simplenote.swift | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Simplenote/Options.swift b/Simplenote/Options.swift index 00f127308..76d95a0aa 100644 --- a/Simplenote/Options.swift +++ b/Simplenote/Options.swift @@ -140,6 +140,19 @@ extension Options { NotificationCenter.default.post(name: .FontSizeDidChange, object: nil) } } + + /// Index notes for spotlight + /// + @objc + var indexNotesForSpotlight: Bool { + get { + defaults.bool(forKey: .indexNotesForSpotlight) + } + + set { + defaults.set(newValue, forKey: .indexNotesForSpotlight) + } + } } // MARK: - Migrations diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 3856aa198..42015dc2a 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -579,6 +579,9 @@ - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window - (void)indexSpotlightItemsIfNeeded { + if ([[Options shared] indexNotesForSpotlight] == NO) { + return; + } // This process should be executed *just once*, and only if the user is already logged in (AKA "Upgrade") NSString *kSpotlightDidRunKey = @"SpotlightDidRunKey"; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; @@ -602,13 +605,7 @@ - (void)indexSpotlightItems NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [context setParentContext:self.simperium.managedObjectContext]; - [context performBlock:^{ - NSArray *deleted = [context fetchObjectsForEntityName:@"Note" withPredicate:[NSPredicate predicateWithFormat:@"deleted == YES"] error:nil]; - [[CSSearchableIndex defaultSearchableIndex] deleteSearchableNotes:deleted]; - - NSArray *notes = [context fetchObjectsForEntityName:@"Note" withPredicate:[NSPredicate predicateWithFormat:@"deleted == NO"] error:nil]; - [[CSSearchableIndex defaultSearchableIndex] indexSearchableNotes:notes]; - }]; + [[CSSearchableIndex defaultSearchableIndex] indexSpotlightItemsIn:context]; } @end diff --git a/Simplenote/UserDefaults+Simplenote.swift b/Simplenote/UserDefaults+Simplenote.swift index 34c6f213f..5fff0fa2c 100644 --- a/Simplenote/UserDefaults+Simplenote.swift +++ b/Simplenote/UserDefaults+Simplenote.swift @@ -14,6 +14,7 @@ extension UserDefaults { case statusBarHidden case themeName = "VSThemeManagerThemePrefKey" case fontSize = "kFontSizePreferencesKey" + case indexNotesForSpotlight } } From 057c4b5769a45806d05e5ae911b95f6fbff46cc5 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Mon, 15 Apr 2024 12:52:44 -0600 Subject: [PATCH 11/18] Put checks to user options about indexing notes in CSSearchableIndex --- Simplenote/CSSearchable+Helpers.swift | 25 +++++++++++++++++++++++++ Simplenote/SimplenoteAppDelegate.m | 3 --- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Simplenote/CSSearchable+Helpers.swift b/Simplenote/CSSearchable+Helpers.swift index 01c2465cc..0ca4a2417 100644 --- a/Simplenote/CSSearchable+Helpers.swift +++ b/Simplenote/CSSearchable+Helpers.swift @@ -31,8 +31,29 @@ extension CSSearchableItem { } extension CSSearchableIndex { + // MARK: - Index Notes + @objc + func indexSpotlightItems(in context: NSManagedObjectContext) { + guard Options.shared.indexNotesForSpotlight else { + return + } + + context.perform { + if let deleted = try? context.fetchObjects(for: "Note", withPredicate: NSPredicate(format: "deleted == YES")) as? [Note] { + CSSearchableIndex.default().deleteSearchableNotes(deleted) + } + + if let notes = try? context.fetchObjects(for: "Note", withPredicate: NSPredicate(format: "deleted == NO")) as? [Note] { + CSSearchableIndex.default().indexSearchableNotes(notes) + } + } + } @objc func indexSearchableNote(_ note: Note) { + guard Options.shared.indexNotesForSpotlight else { + return + } + let item = CSSearchableItem(note: note) indexSearchableItems([item]) { error in if let error = error { @@ -42,6 +63,10 @@ extension CSSearchableIndex { } @objc func indexSearchableNotes(_ notes: [Note]) { + guard Options.shared.indexNotesForSpotlight else { + return + } + let items = notes.map { return CSSearchableItem(note: $0) } diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index 42015dc2a..e9016f9c8 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -579,9 +579,6 @@ - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window - (void)indexSpotlightItemsIfNeeded { - if ([[Options shared] indexNotesForSpotlight] == NO) { - return; - } // This process should be executed *just once*, and only if the user is already logged in (AKA "Upgrade") NSString *kSpotlightDidRunKey = @"SpotlightDidRunKey"; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; From e72f78e24b9b8dab5c4b1b7796f073ba23080d63 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Mon, 15 Apr 2024 13:08:20 -0600 Subject: [PATCH 12/18] Added setting to enable and disable indexing notes in spotlight --- Simplenote/Preferences.storyboard | 41 ++++++++++++++-------- Simplenote/PreferencesViewController.swift | 22 +++++++++++- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Simplenote/Preferences.storyboard b/Simplenote/Preferences.storyboard index d6aef3ba9..8d6b1f962 100644 --- a/Simplenote/Preferences.storyboard +++ b/Simplenote/Preferences.storyboard @@ -1,8 +1,8 @@ - + - + @@ -38,14 +38,14 @@ - + - + - + @@ -97,10 +97,10 @@ - + - + @@ -108,7 +108,7 @@ - + @@ -116,7 +116,7 @@ - + @@ -127,7 +127,7 @@ + + @@ -178,12 +189,13 @@ - + + @@ -353,6 +365,7 @@ + diff --git a/Simplenote/PreferencesViewController.swift b/Simplenote/PreferencesViewController.swift index f8850e5ea..e2ca3554e 100644 --- a/Simplenote/PreferencesViewController.swift +++ b/Simplenote/PreferencesViewController.swift @@ -1,4 +1,5 @@ import Cocoa +import CoreSpotlight class PreferencesViewController: NSViewController { private var simperium: Simperium { @@ -32,7 +33,8 @@ class PreferencesViewController: NSViewController { @IBOutlet private var themePopUp: NSPopUpButton! @IBOutlet private var textSizeSlider: NSSlider! @IBOutlet private var shareAnalyticsCheckbox: NSButton! - + @IBOutlet weak var indexNotesButton: NSButtonCell! + // MARK: Background Views @IBOutlet private var accountSectionBackground: BackgroundView! @IBOutlet private var layoutSectionBackground: BackgroundView! @@ -65,6 +67,7 @@ class PreferencesViewController: NSViewController { updateLineLength() updateCondensedNoteListCheckBox() updateSortTagsAlphabeticallyCheckbox() + updateIndexNotesCheckbox() updateSelectedTheme() @@ -170,6 +173,10 @@ class PreferencesViewController: NSViewController { sortTagsAlphabeticallyCheckbox.state = Options.shared.alphabeticallySortTags ? .on : .off } + private func updateIndexNotesCheckbox() { + indexNotesButton.state = Options.shared.indexNotesForSpotlight ? .on : .off + } + @objc private func updateTextSizeSlider() { textSizeSlider.intValue = Int32(Options.shared.fontSize) @@ -293,6 +300,19 @@ class PreferencesViewController: NSViewController { let isEnabled = sender.state == .on Options.shared.analyticsEnabled = isEnabled } + + @IBAction func indexNotesWasPressed(_ sender: Any) { + Options.shared.indexNotesForSpotlight.toggle() + + if Options.shared.indexNotesForSpotlight { + let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + context.parent = SimplenoteAppDelegate.shared().simperium.managedObjectContext() + + CSSearchableIndex.default().indexSpotlightItems(in: context) + } else { + CSSearchableIndex.default().deleteAllSearchableItems() + } + } } private struct Strings { From e73e93133764899f5d82128d600f357bbf9faa54 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Mon, 15 Apr 2024 13:11:35 -0600 Subject: [PATCH 13/18] Fixed a linting error --- Simplenote/PreferencesViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Simplenote/PreferencesViewController.swift b/Simplenote/PreferencesViewController.swift index e2ca3554e..bf2792c35 100644 --- a/Simplenote/PreferencesViewController.swift +++ b/Simplenote/PreferencesViewController.swift @@ -34,7 +34,7 @@ class PreferencesViewController: NSViewController { @IBOutlet private var textSizeSlider: NSSlider! @IBOutlet private var shareAnalyticsCheckbox: NSButton! @IBOutlet weak var indexNotesButton: NSButtonCell! - + // MARK: Background Views @IBOutlet private var accountSectionBackground: BackgroundView! @IBOutlet private var layoutSectionBackground: BackgroundView! From f7dacb3efe79bf9675c0b24b7de7b24beba40937 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Wed, 17 Apr 2024 11:32:39 -0300 Subject: [PATCH 14/18] PreferencesViewController: Fixes UI / Options Out o Sync Glitch --- Simplenote/PreferencesViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Simplenote/PreferencesViewController.swift b/Simplenote/PreferencesViewController.swift index bf2792c35..12013673a 100644 --- a/Simplenote/PreferencesViewController.swift +++ b/Simplenote/PreferencesViewController.swift @@ -302,9 +302,14 @@ class PreferencesViewController: NSViewController { } @IBAction func indexNotesWasPressed(_ sender: Any) { - Options.shared.indexNotesForSpotlight.toggle() + guard let sender = sender as? NSButton else { + return + } + + let isEnabled = sender.state == .on + Options.shared.indexNotesForSpotlight = isEnabled - if Options.shared.indexNotesForSpotlight { + if isEnabled { let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) context.parent = SimplenoteAppDelegate.shared().simperium.managedObjectContext() From cddcbedcc4a7788df1582196f048c428969d40a3 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Wed, 17 Apr 2024 09:13:07 -0600 Subject: [PATCH 15/18] refactored CSSearchIndex helpers to use self instead of calling default --- Simplenote/CSSearchable+Helpers.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Simplenote/CSSearchable+Helpers.swift b/Simplenote/CSSearchable+Helpers.swift index 0ca4a2417..77daf31a3 100644 --- a/Simplenote/CSSearchable+Helpers.swift +++ b/Simplenote/CSSearchable+Helpers.swift @@ -40,11 +40,11 @@ extension CSSearchableIndex { context.perform { if let deleted = try? context.fetchObjects(for: "Note", withPredicate: NSPredicate(format: "deleted == YES")) as? [Note] { - CSSearchableIndex.default().deleteSearchableNotes(deleted) + self.deleteSearchableNotes(deleted) } if let notes = try? context.fetchObjects(for: "Note", withPredicate: NSPredicate(format: "deleted == NO")) as? [Note] { - CSSearchableIndex.default().indexSearchableNotes(notes) + self.indexSearchableNotes(notes) } } } From d570f24967a963945a6bf6d22f50f11214d376a8 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Wed, 17 Apr 2024 09:14:26 -0600 Subject: [PATCH 16/18] Refactored CSSearchable helpers to drop force unwrap --- Simplenote/CSSearchable+Helpers.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Simplenote/CSSearchable+Helpers.swift b/Simplenote/CSSearchable+Helpers.swift index 77daf31a3..dc87d0605 100644 --- a/Simplenote/CSSearchable+Helpers.swift +++ b/Simplenote/CSSearchable+Helpers.swift @@ -83,9 +83,7 @@ extension CSSearchableIndex { } @objc func deleteSearchableNotes(_ notes: [Note]) { - let ids = notes.map { - return $0.simperiumKey! - } + let ids = notes.compactMap({ $0.simperiumKey }) deleteSearchableItems(withIdentifiers: ids) { error in if let error = error { From 2e2f0e87f09f23cb2bbbb4613399e9dfdb159ecf Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Wed, 17 Apr 2024 09:17:36 -0600 Subject: [PATCH 17/18] Dropped indexing in spotlight migration because the option is switched --- Simplenote/SimplenoteAppDelegate.m | 35 ------------------------------ 1 file changed, 35 deletions(-) diff --git a/Simplenote/SimplenoteAppDelegate.m b/Simplenote/SimplenoteAppDelegate.m index bc08f6d1e..376a760ae 100644 --- a/Simplenote/SimplenoteAppDelegate.m +++ b/Simplenote/SimplenoteAppDelegate.m @@ -124,8 +124,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification [self cleanupTags]; [self startListeningForThemeNotifications]; - [self indexSpotlightItemsIfNeeded]; - [SPTracker trackApplicationLaunched]; } @@ -573,37 +571,4 @@ - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window return [[self managedObjectContext] undoManager]; } - -#pragma mark ================================================================================ -#pragma mark Spotlight -#pragma mark ================================================================================ - -- (void)indexSpotlightItemsIfNeeded -{ - // This process should be executed *just once*, and only if the user is already logged in (AKA "Upgrade") - NSString *kSpotlightDidRunKey = @"SpotlightDidRunKey"; - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - if ([defaults boolForKey:kSpotlightDidRunKey] == true) { - return; - } - - [defaults setBool:true forKey:kSpotlightDidRunKey]; - [defaults synchronize]; - - if (self.simperium.user.authenticated == false) { - return; - } - - [self indexSpotlightItems]; -} - -- (void)indexSpotlightItems -{ - NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; - [context setParentContext:self.simperium.managedObjectContext]; - - [[CSSearchableIndex defaultSearchableIndex] indexSpotlightItemsIn:context]; -} - @end From fd0a4f6bd522e9697606b3eb992848fe584e2aa5 Mon Sep 17 00:00:00 2001 From: Charlie Scheer Date: Wed, 17 Apr 2024 09:19:57 -0600 Subject: [PATCH 18/18] Refactored handleUserActivity to unwrap the simperium key for index --- Simplenote/SimplenoteAppDelegate+Swift.swift | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Simplenote/SimplenoteAppDelegate+Swift.swift b/Simplenote/SimplenoteAppDelegate+Swift.swift index 01bf512a7..7acb8e1ab 100644 --- a/Simplenote/SimplenoteAppDelegate+Swift.swift +++ b/Simplenote/SimplenoteAppDelegate+Swift.swift @@ -246,21 +246,14 @@ extension SimplenoteAppDelegate { @objc func handleUserActivity(_ userActivity: NSUserActivity) -> Bool { - if userActivity.activityType == CSSearchableItemActionType { - presentNote(for: userActivity) + if userActivity.activityType == CSSearchableItemActionType, + let simperiumKey = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String { + displayNote(simperiumKey: simperiumKey) return true } return false } - - func presentNote(for userActivity: NSUserActivity) { - guard let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String else { - return - } - - displayNote(simperiumKey: uniqueIdentifier) - } } // MARK: - URL Handlers