aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--QSThingsPlugin.xcodeproj/project.pbxproj1
-rw-r--r--QSThingsPlugin/Actions/CompleteTaskAction.h2
-rw-r--r--QSThingsPlugin/Actions/CompleteTaskAction.m10
-rw-r--r--QSThingsPlugin/Actions/SearchThingsAction.h2
-rw-r--r--QSThingsPlugin/Actions/SearchThingsAction.m6
-rw-r--r--QSThingsPlugin/Actions/ShowInThingsAction.h1
-rw-r--r--QSThingsPlugin/Actions/ShowInThingsAction.m6
-rw-r--r--QSThingsPlugin/Helpers/KeychainHelper.h7
-rw-r--r--QSThingsPlugin/Helpers/KeychainHelper.m7
-rw-r--r--QSThingsPlugin/Helpers/ThingsHelper.h34
-rw-r--r--QSThingsPlugin/Helpers/ThingsHelper.m70
11 files changed, 116 insertions, 30 deletions
diff --git a/QSThingsPlugin.xcodeproj/project.pbxproj b/QSThingsPlugin.xcodeproj/project.pbxproj
index ced85a8..e46957e 100644
--- a/QSThingsPlugin.xcodeproj/project.pbxproj
+++ b/QSThingsPlugin.xcodeproj/project.pbxproj
@@ -45,6 +45,7 @@
Actions/ShowInThingsAction.m,
Helpers/KeychainHelper.m,
Helpers/SQLHelper.m,
+ Helpers/ThingsHelper.m,
Parsers/QSThingsAreasParser.m,
Parsers/QSThingsProjectsParser.m,
Parsers/QSThingsTasksParser.m,
diff --git a/QSThingsPlugin/Actions/CompleteTaskAction.h b/QSThingsPlugin/Actions/CompleteTaskAction.h
index 19609b9..e542c30 100644
--- a/QSThingsPlugin/Actions/CompleteTaskAction.h
+++ b/QSThingsPlugin/Actions/CompleteTaskAction.h
@@ -1,5 +1,5 @@
#import "Constants.h"
-#import "KeychainHelper.h"
+#import "ThingsHelper.h"
#import <Foundation/Foundation.h>
#import <QSCore/QSCore.h>
diff --git a/QSThingsPlugin/Actions/CompleteTaskAction.m b/QSThingsPlugin/Actions/CompleteTaskAction.m
index 9673840..5ff1bd0 100644
--- a/QSThingsPlugin/Actions/CompleteTaskAction.m
+++ b/QSThingsPlugin/Actions/CompleteTaskAction.m
@@ -15,17 +15,15 @@ NS_ASSUME_NONNULL_BEGIN
if (authenticationKey && [validTypes containsObject:directObject.primaryType]) {
NSDictionary *contents = [directObject objectForType:directObject.primaryType];
- NSString *path = @"update";
+ ThingsURLRoute route = ThingsURLRouteUpdate;
if (directObject.primaryType == kProjectType) {
- path = @"update-project";
+ route = ThingsURLRouteUpdateProject;
}
if (contents) {
NSString *uuid = [contents objectForKey:@"uuid"];
if (uuid) {
- NSString *urlString = [NSString stringWithFormat:@"things:///%@?id=%@&auth-token=%@&completed=true", path, uuid, authenticationKey];
- NSURL *url = [NSURL URLWithString:urlString];
-
- [[NSWorkspace sharedWorkspace] openURL:url];
+ NSDictionary *parameters = @{@"id": uuid, @"completed": @"true"};
+ [ThingsHelper callRoute:route withParameters:parameters];
} else {
NSLog(@"Things object did not have a uuid.");
}
diff --git a/QSThingsPlugin/Actions/SearchThingsAction.h b/QSThingsPlugin/Actions/SearchThingsAction.h
index 5f0ed48..c35bb1a 100644
--- a/QSThingsPlugin/Actions/SearchThingsAction.h
+++ b/QSThingsPlugin/Actions/SearchThingsAction.h
@@ -1,4 +1,4 @@
-#import "Constants.h"
+#import "ThingsHelper.h"
#import <Foundation/Foundation.h>
#import <QSCore/QSCore.h>
diff --git a/QSThingsPlugin/Actions/SearchThingsAction.m b/QSThingsPlugin/Actions/SearchThingsAction.m
index 9b042b3..9f941e7 100644
--- a/QSThingsPlugin/Actions/SearchThingsAction.m
+++ b/QSThingsPlugin/Actions/SearchThingsAction.m
@@ -9,10 +9,8 @@ NS_ASSUME_NONNULL_BEGIN
NSString *contents = [directObject displayName];
if (contents) {
- NSString *urlString = [NSString stringWithFormat:@"things:///search?query=%@", contents];
- NSURL *url = [NSURL URLWithString:urlString];
-
- [[NSWorkspace sharedWorkspace] openURL:url];
+ NSDictionary *parameters = @{@"query": contents};
+ [ThingsHelper callRoute:ThingsURLRouteSearch withParameters:parameters];
} else {
NSLog(@"Could not get the text of the Things object.");
}
diff --git a/QSThingsPlugin/Actions/ShowInThingsAction.h b/QSThingsPlugin/Actions/ShowInThingsAction.h
index 8d7790c..7a2fbc4 100644
--- a/QSThingsPlugin/Actions/ShowInThingsAction.h
+++ b/QSThingsPlugin/Actions/ShowInThingsAction.h
@@ -1,4 +1,5 @@
#import "Constants.h"
+#import "ThingsHelper.h"
#import <Foundation/Foundation.h>
#import <QSCore/QSCore.h>
diff --git a/QSThingsPlugin/Actions/ShowInThingsAction.m b/QSThingsPlugin/Actions/ShowInThingsAction.m
index 91fe3ec..cd04f73 100644
--- a/QSThingsPlugin/Actions/ShowInThingsAction.m
+++ b/QSThingsPlugin/Actions/ShowInThingsAction.m
@@ -14,10 +14,8 @@ NS_ASSUME_NONNULL_BEGIN
if (contents) {
NSString *uuid = [contents objectForKey:@"uuid"];
if (uuid) {
- NSString *urlString = [NSString stringWithFormat:@"things:///show?id=%@", uuid];
- NSURL *url = [NSURL URLWithString:urlString];
-
- [[NSWorkspace sharedWorkspace] openURL:url];
+ NSDictionary *parameters = @{@"id": uuid};
+ [ThingsHelper callRoute:ThingsURLRouteShow withParameters:parameters];
} else {
NSLog(@"Things object did not have a uuid.");
}
diff --git a/QSThingsPlugin/Helpers/KeychainHelper.h b/QSThingsPlugin/Helpers/KeychainHelper.h
index d935bec..3665377 100644
--- a/QSThingsPlugin/Helpers/KeychainHelper.h
+++ b/QSThingsPlugin/Helpers/KeychainHelper.h
@@ -1,10 +1,3 @@
-//
-// KeychainHelper.h
-// Things Plugin
-//
-// Created by Ruben Beltran del Rio on 2025-09-10.
-//
-
#import "Constants.h"
#import <Foundation/Foundation.h>
#import <Security/Security.h>
diff --git a/QSThingsPlugin/Helpers/KeychainHelper.m b/QSThingsPlugin/Helpers/KeychainHelper.m
index 9ce90f7..fcf8291 100644
--- a/QSThingsPlugin/Helpers/KeychainHelper.m
+++ b/QSThingsPlugin/Helpers/KeychainHelper.m
@@ -1,10 +1,3 @@
-//
-// KeychainHelper.m
-// Things Plugin
-//
-// Created by Ruben Beltran del Rio on 2025-09-10.
-//
-
#import "KeychainHelper.h"
NS_ASSUME_NONNULL_BEGIN
diff --git a/QSThingsPlugin/Helpers/ThingsHelper.h b/QSThingsPlugin/Helpers/ThingsHelper.h
new file mode 100644
index 0000000..29d3092
--- /dev/null
+++ b/QSThingsPlugin/Helpers/ThingsHelper.h
@@ -0,0 +1,34 @@
+#import "KeychainHelper.h"
+#import "Constants.h"
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Enum of valid Things URL routes.
+ */
+typedef NSString* ThingsURLRoute NS_STRING_ENUM;
+
+extern ThingsURLRoute const ThingsURLRouteSearch;
+extern ThingsURLRoute const ThingsURLRouteShow;
+extern ThingsURLRoute const ThingsURLRouteUpdate;
+extern ThingsURLRoute const ThingsURLRouteUpdateProject;
+extern ThingsURLRoute const ThingsURLRouteAdd;
+extern ThingsURLRoute const ThingsURLRouteAddProject;
+
+/**
+ Helper class to communicate with things using the URL API
+ */
+@interface ThingsHelper : NSObject
+
+/**
+ Calls a Things URL route with the specified parameters
+ @param route the Things URL route to call. Use ThingsURLRoute enum.
+ @param parameters NSDictionary of query parameters to include in the URL. Can be nil or empty.
+ @returns YES if the URL was successfully opened, NO if something went wrong.
+ */
++ (BOOL)callRoute:(ThingsURLRoute)route withParameters:(NSDictionary *)parameters;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/QSThingsPlugin/Helpers/ThingsHelper.m b/QSThingsPlugin/Helpers/ThingsHelper.m
new file mode 100644
index 0000000..cc7267b
--- /dev/null
+++ b/QSThingsPlugin/Helpers/ThingsHelper.m
@@ -0,0 +1,70 @@
+#import "ThingsHelper.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+ThingsURLRoute const ThingsURLRouteSearch = @"search";
+ThingsURLRoute const ThingsURLRouteShow = @"show";
+ThingsURLRoute const ThingsURLRouteUpdate = @"update";
+ThingsURLRoute const ThingsURLRouteUpdateProject = @"update-project";
+ThingsURLRoute const ThingsURLRouteAdd = @"add";
+ThingsURLRoute const ThingsURLRouteAddProject = @"add-project";
+
+@implementation ThingsHelper
+
++ (instancetype)sharedInstance {
+ static ThingsHelper *sharedInstance = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sharedInstance = [[self alloc] init];
+ });
+ return sharedInstance;
+}
+
++ (BOOL)requiresAuthentication:(ThingsURLRoute)route {
+ if ([route isEqualToString:ThingsURLRouteSearch] || [route isEqualToString:ThingsURLRouteShow]) {
+ return NO;
+ }
+ return YES;
+}
+
++ (BOOL)callRoute:(ThingsURLRoute)route withParameters:(NSDictionary *)parameters {
+ NSString *authenticationKey = [KeychainHelper authenticationKey];
+ if ([self requiresAuthentication:route] && !authenticationKey) {
+ NSLog(@"Attempted to call an authenticated things URL without an authentication key.");
+ return NO;
+ }
+
+ NSString *baseURL = [NSString stringWithFormat:@"things:///%@", route];
+ NSMutableArray *queryComponents = [NSMutableArray array];
+
+ if ([self requiresAuthentication:route]) {
+ [queryComponents addObject:[NSString stringWithFormat:@"auth-token=%@", authenticationKey]];
+ }
+
+ for (NSString *key in parameters) {
+ id value = parameters[key];
+ NSString *encodedKey = [self urlEncodeString:key];
+ NSString *encodedValue = [self urlEncodeString:[NSString stringWithFormat:@"%@", value]];
+ [queryComponents addObject:[NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue]];
+ }
+
+ NSString *urlString = baseURL;
+ if ([queryComponents count] > 0) {
+ urlString = [NSString stringWithFormat:@"%@?%@", baseURL, [queryComponents componentsJoinedByString:@"&"]];
+ }
+
+ NSURL *url = [NSURL URLWithString:urlString];
+
+ [[NSWorkspace sharedWorkspace] openURL:url];
+
+ return YES;
+}
+
++ (NSString *)urlEncodeString:(NSString *)string {
+ NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet];
+ return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END