Getting last edited Draft via URL Scheme

My goal is to append the Text selected in Mac OS to the last edited draft (meaning the draft with the most recent date of modification).

On iOS, I could achieve this via Shortcuts, as the “Search Drafts” actions allows to get the UUID of the most recently edited Draft, which can then be passed along to the append action of Shortcuts.

However, I am trying to figure out how to do this on Mac. As far as I can tell from the Drafts documentation, controlling Drafts can be achieved via Applescript and via URL Schemes. As Applescript is still limited to Draft creation only, I would have to use URL Schemes. So using this URL Scheme allows me to add text if I already have the UUID of a Draft:

drafts://x-callback-url/append?uuid=UUID-TO-VALID-DRAFT&text=TEXT-TO-ADD

But going through the URL Scheme Documentation, I couldn’t figure out how to get the UUID of the last edited Draft, to use it in the append-URL-Scheme.

One possible method I could think of was to use the /runaction-URL-Scheme to somehow get the needed UUID. But I couldn’t really find out how write such an action – draft.find also depends on already having an UUID as well.

Any ideas? (just for context; I am thinking of using an Alfred Workflow or Custom Popclip Extension to call the URL-Scheme with the selected text.)

draft.query should give you the most recent modified draft:

let drafts = draft.query("", "all", [], [], "modified", true);

This returns an array of drafts with the most recent modified draft at the first index.

1 Like

Presumably, with that, you could compare to “now” and decide whether to create a new draft base on how long since the last draft was updated. The only advantage over the built in support is that you could combine that logic with e.g. location.

Note that query() is a static method, and so it should be Draft.query, not draft.query.

I agree that this would be a good way to find the last modified draft, and that a URL scheme call is probably the best way to go at the moment given the constraints on AppleScript.

Here’s a quick function based on updating the last modified draft if one is available.

function appendToLastModifiedDraft(p_strTextToAppend)
{
	let adraftModified = Draft.query("", "all", [], [], "modified", true);
	if (adraftModified.length > 0)
	{
		adraftModified[0].append(p_strTextToAppend);
		adraftModified[0].update();
		return true;
	}
	else 
	{
		app.displayErrorMessage("No drafts found");
		return false;
	}
}

Here’s an action that utilises it to append the current draft content to the last modified content.

To make use of this from Alfred, it is just a two step workflow. Here I selected a hotkey trigger (purposefully an awkward one to ensure it didn’t clash with any of my existing ones :laughing: ).

2020-11-07-09.45.11

The hotkey passes in the selected text.

2020-11-07-09.45.55

And the URL executed looks like this:

drafts://x-callback-url/runAction?text={query}&action=Append%20Draft%20to%20Last%20Modified

2020-11-07-09.47.25

You can download the Alfred Workflow here, and do modify that shortcut key combination if you do :wink:

Hope that helps.

3 Likes

You’re a hero, thats great, thanks a lot!

So it didn’t take much to make a working Popclip Extension out of it; just in case anyone is interested or someone finds this thread via search, here is the Popclip Extension:


two minor questions concerning your solution:

  1. So my idea was that instead of displaying an error, Drafts should create a new Draft if the query for the last modified drafts comes back empty. Thought this would be straightforward, basically replacing the error message in the else-part of your script with a Draft constructor. But actually this does not work. Testing your script, I noticed that when there is no Draft available, not even the error message gets posted for me. So either sth with the else-part is wrong, or more likely something isn’t configured right in my Drafts app?
  1. For convenience, I wanted to add that the cursor in Drafts moves to the position where I can easily add page references. I already figured out that this would be the respective code:

    editor.setSelectedRange(editor.getText().length - 1, 0);

    It works when I tested it individually, but together with this script here, there seems to be a timing error, as the cursor is moved first and then the text appended, leaving the cursor at the totally wrong position. Even though I added the line just after the last line in the Script of the Draft action you uploaded. I assume I have to add a short delay to let Drafts catch up, but Javascript’s setTimeout() doesn’t seem to work in Drafts?

Without seeing what you did it’s incredibly difficult to actually intuit the issue.

I created a modified version of the action with a modified version of the function that worked just fine for my test.

function appendToLastModifiedDraft(p_strTextToAppend)
{
	//I've added a tag match in here for testing purposes to ensure a recently modified draft is not found.
	let adraftModified = Draft.query("", "all", ["idonothaveadraftwiththistag"], [], "modified", true);
	if (adraftModified.length > 0)
	{
		adraftModified[0].append(p_strTextToAppend);
		adraftModified[0].update();
		return true;
	}
	else 
	{
		//Create a new draft, set its content and update the draft to store it		
		let draftNew = new Draft();
		draftNew.content = p_strTextToAppend;
		draftNew.update();
		return false;
	}
}

appendToLastModifiedDraft(draft.content);

The code is working with drafts in Drafts, and not the draft currently loaded in the editor. You are combining very different things. Think of it like the previous code is working with the data, and the editor code is working with the app’s user interface, and that user interface doesn’t even necessarily have that data loaded into it at the time.

After the original code has executed, you would want to load the last modified draft into the editor. Once loaded, you would then use your code, which seems to place the cursor one character from the end of the draft.

Hopefully, that shouldn’t be any sort of timing issue with that, but if you do get anything, do post back with the details of what you have tried and what you have observed.

JavaScript’s setTimeout() is a window or global worker scoped function/method. Drafts is not a browser. It does not have these, hence why it wouldn’t work with Drafts. There are some sleep functions in TADpoLe that you could use if you do want to introduce a delay in code execution.

Hope that helps.

1 Like

Nitpicky, I know, but s/status method/static method/ - lest anybody be confused.

1 Like

Not nitpicky at all, and not at all what I remembered typing. I strongly suspect an auto correct triggered by my truly atrocious typing.

I’ve amended it in the post.

2 Likes

I got both issues resolved, thanks a lot!

So the first issue drove me crazy as I came up with exactly the same solution, but it didn’t work. After about an hour I noticed that the issue was that I changed the Draft.query to search the inbox, and I actually had some Drafts in the Inbox (my shopping lists), which were just hidden away in that workspace. And voilá, my last modified shopping list has been collecting my quotations the whole time. Silly me. :upside_down_face: (As the action was actually running properly, that was also the reason no error message appeared.)

And yeah the second issue got solved by activating and loading the most recent draft into the editor. Wasn’t aware of the difference between what the code is working on and what is displayed in the editor. Got it now.

Thanks a lot for the support! I am only something like a “casual coder” and my experience does not go beyond the basics of coding I learned in some high school classes. For reference for other people reading this thread, here is the final code:

// Drafts URL Scheme used in Alfred: 
// drafts://x-callback-url/runAction?text=-%20"{query}"%20(p.%20)&action=Append%20Draft%20to%20Last%20Modified
//this results in "(p. )" beeing added at the end for page reference

// Append the content of the current draft to the last modified draft. 
// if there is currently no draft in the Inbox, a new one is created. 

function appendToLastModifiedDraft(p_strTextToAppend)
{
	let adraftModified = Draft.query("", "inbox", ["quotations"], [], "modified", true);
	if (adraftModified.length > 0)
	{
		adraftModified[0].append(p_strTextToAppend);
		adraftModified[0].update();
		return true;
	}
	else 
	{
		//Create a new draft, set its content and update the draft to store it
		let draftNew = new Draft();
		draftNew.content = p_strTextToAppend;
		draftNew.addTag ("quotations");
		draftNew.update();
		return false;
	}
}

appendToLastModifiedDraft(draft.content);

//loads the modified draft in the editor
editor.activate();
theModifiedDraft = Draft.query("", "inbox", ["quotations"], [], "modified", true);
editor.load (theModifiedDraft[0]);

//puts the cursor at the right spot for adding page reference, for convenience
editor.setSelectedRange(editor.getText().length - 1, 0);