Writing a script to add a note to Bear app

Hi,

I’m writing a script to add notes to the Bear app. The user is prompted to make one of two choices - either to add the item(s) to an existing note or to create a new note. Depending on which option is chosen, a further prompt would appear offering the option to enter/select further options, e.g. flag, title, etc. These would then be mapped to URL parameters to be added to the final URL.

I have written the skeleton of a script (below) and I’m reasonably sure how to generate various input fields for the prompt; however, I’m unsure as to how to do the nested prompt (or even if it’s possible).


let p = Prompt.create();
p.addButton("Append to note");
p.addButton("Create a new note");
if (p.show() === false)
{
	context.cancel();
}

let cb = CallbackURL.create();
cb.baseURL = "bear://x-callback-url/";

switch (p.buttonPressed)
{
	case "Append to note":
	cb.addParameter("add-text")
		break;
	case "Create a new note":
	cb.addParameter("create");
		break;
}
cb.addParameter("text", draft.processTemplate("[[draft]]"));

if (cb.open())
{
	console.log("Item added to Bear")
}
else
{
	console.log(cb.status);
	if (cb.status == "cancel")
	{
		context.cancel();
	}
	else
	{
		context.fail();
	}
}

Any guidance would be greatly appreciated!

Many thanks,

Martin

Create a new prompt instance named something other than p (e.g. p2), add whatever fields or buttons you require, and do this within your switch case(s) that requires whatever additional data you wish to capture.

1 Like

@sylumer Okay, so this is what I have so far:

let p = Prompt.create();
p.addButton("Append to note");
p.addButton("Create a new note");
if (p.show() === false)
{
	context.cancel();
}

let cb = CallbackURL.create();
cb.baseURL = "bear://x-callback-url/";

switch (p.buttonPressed)
{
	case "Append to note":
	let p2 = Prompt.create();
   p2.addTextField("textFieldName","Title:","");
   p2.addTextField("textFieldName2","Tags:","");
   p2.addTextField("textFieldName3","File:","");
   p2.addTextField("textFieldName4","Filename:","");
   p2.addTextField("textFieldName5","Header:","");
   p2.addPicker("picker", "Mode:", [["append", "prepend", "replace", "replace_all"]], [0]);

   p2.addButton("OK");

   p2.show();

   cb.addParameter("add-text")
   cb.addParameter("title","textFieldName");
   cb.addParameter("tags","textFieldName2");
   cb.addParameter("file","textFieldName3");
   cb.addParameter("filename","textFieldName4");
   cb.addParameter("header","textFieldName5");
   cb.addParameter("mode","picker")
      break;
      
	case "Create a new note":
	let p3 = Prompt.create();
   p3.addTextField("textFieldName","Title:","");
   p3.addTextField("textFieldName2","Tags:","");
   p3.addTextField("textFieldName3","File:","");
   p3.addTextField("textFieldName4","Filename:","");
   p3.addSwitch("sw", "Pin?", false);

   p3.addButton("OK");

   p3.show();

   cb.addParameter("create");
   cb.addParameter("title","textFieldName");
   cb.addParameter("tags","textFieldName2");
   cb.addParameter("file","textFieldName3");
   cb.addParameter("filename","textFieldName4");
   cb.addParameter("pin","sw")
      break;
}

cb.addParameter("text", draft.processTemplate("[[draft]]"));

if (cb.open())
{
	console.log("Item added to Bear")
}
else
{
	console.log(cb.status);
	if (cb.status == "cancel")
	{
		context.cancel();
	}
	else
	{
		context.fail();
	}
}

It’s a work in progress, so any advice would be greatly appreciated!

Thanks,

Martin

Check this:

https://actions.getdrafts.com/a/16G

:nerd_face:

1 Like

@martinperry

This will not abort current script, only abort other action steps that might be following this one.

Try to wrap the script in a self-invoking function.
Then you can abort script gracefully by adding return; after context.cancel(); :

(() => {
  ...
  if (p.show() === false) { 
    context.cancel(); 
    return;
  }
  ...
})()

// or if you want it less anonymous:

(function main() {
  ...
  if (p.show() === false) { 
    context.cancel();
    return;
  }
  ...
})()

Wrapping scripts in a function also stops variables for being global and leaking over to other script steps coming later in the same action. Global vars are accessible to other script steps in same Drafts action, which is great when you want to pass results from one step to another. Just read on this forum, thanks to @draft8

I also just learned in same tread that it will make your scripts run faster. See: js-simpler-interactions-between-2-or-more-script-steps

	cb.addParameter("add-text")
	cb.addParameter("create")

Are not valid parameters for Bear x-callback.
Do this instead:

...

let cb = CallbackURL.create();

switch (p.buttonPressed) {
  case "Append to note":
    cb.baseURL = "bear://x-callback-url/add-text";
    break;
  case "Create a new note":
    cb.baseURL = "bear://x-callback-url/create";
    break;
}

cb.addParameter("text", draft.processTemplate("[[draft]]"));

...

Have a look at the complete action: Create or Append draft in Bear, which also shows nesting of prompts.

Adding files to bear in your draft script will be quite difficult I believe.

Probably easier to open bear with a new url action step after this script, and then paste file (copied from Finder, macOS) or insert directly in Bear editor (using the paperclip icon, iOS)

Therefore the file fields and steps have been commented out. (It might be possible, but it’s above my paygrade;)

Hope that’s helpful.

2 Likes

@RoyRogers It’s incredibly helpful - thank you so much for your time and effort. I also thank you for your advice - I agree that the user experience is much more elegant using a function, and I also suspect adding files can be handled in a better way.

I’ve made some small modifications to the script you supplied (see below). I’ve made the ‘pin’ parameter apply to both ‘append’ and ‘new’, and I’ve also added an ‘edit’ parameter (set to ‘true’ by default), as I often wish to edit the note in Bear afterwards, and I don’t want to immediately be returned to Drafts.

'use strict';

(() => {
  
  let cb = CallbackURL.create();
  
  loop:
  while(true) { 
    let p = Prompt.create();
    p.addButton("Append to note");
    p.addButton("Create a new note");
    
    if (!p.show()) {
      context.cancel();
      return;
    }
    switch (p.buttonPressed) {
      case "Append to note":
        if (note(cb, 'append')) break loop;
        break;
      
      case "Create a new note":
        if (note(cb, 'new')) break loop;
        break;
    }
  }
  
  cb.addParameter("text", draft.content);
  
  if (cb.open()) {
    console.log("Item added to Bear");
    
  } else {
    console.log(cb.status);
    if (cb.status == "cancel") {
      context.cancel();
    } else {
      context.fail();
    }
  }
})()

function note(cb, type) {
  let p = Prompt.create();
  p.addTextField("title", "Title:", "");
  p.addTextField("tags", "Tags:", "");
  
  let modes = [["append", "prepend", "replace", "replace_all"]];
  if (type == 'append') {
    p.addTextField("header", "Header:", "");    
    p.addPicker("picker", "Mode:", modes, [0]);  
  }
  
  p.addSwitch("sw", "Pin?", false);
  p.addSwitch("sw2", "Edit?", true);
  
  p.addButton("OK");
  
  if (!p.show()) {
    return false;
  
    cb.baseURL = "bear://x-callback-url/create";
  } else {
    cb.baseURL = "bear://x-callback-url/add-text";
    if (p.fieldValues['header'] != '') {
      cb.addParameter("header", p.fieldValues['header']);
    }
    let index = parseInt(p.fieldValues['picker']);
    cb.addParameter("mode", modes[0][index]);
  }

  cb.addParameter("title", p.fieldValues['title']);
  cb.addParameter("tags", p.fieldValues['tags']);
  cb.addParameter("pin", p.fieldValues['sw']);
  cb.addParameter("edit", p.fieldValues['sw2']);
  return true;
}

The problem is that both these switches are being ignored (the former in your original script, too). Is it possibly a problem with mapping true/false (the values returned in Drafts) to yes/no (the parameters expected in Bear)?

Thanks,

Martin

That sounds like quite a quick test to do. Have you simply tried amending a value to test the theory? Presumably an if statement would suffice if you need to do such a binary true/false:yes/no mapping.

1 Like

@martinperry

CallbackURL will return to Draft regardless of this parameter,
but you could add: app.openURL('bear://'); afterwards when you need to open bear.

Or like this:

  ...
  cb.addParameter("text", draft.content);
  // app.setClipboard(cb.url); For testin/debuging url.
  
  let success = false;
  if (edit) {
    // will stay in Bear:
    success = app.openURL(cb.url); 
    // alternatively, but will flash screen:
    // success = cb.open(); // returns to Draft
    // app.openURL('bear://'); // return to Bear
  } else {
    // will return to Drafts:
    success = cb.open();
  }

  if (success) {
    ... 

Yes, you are right: ‘true/false’ is not accepted by Bear, it should be ‘yes/no’:

See the updated action: https://actions.getdrafts.com/a/16G with the new switches.

Also, at ‘create new’ and your draft already have a # heading, title is not neccesary in the prompt.

PS. Thanks, I had fun – learning as I go.
Have a good evening/night/morning
:mask:

1 Like

@RoyRogers @sylumer I made a slight tweak to the code, because I realised that ‘pin’ is not an allowed parameter with ‘add-text’, and now it’s all sweet as a nut. There is no way I would have got there without you guys. Many thanks!

1 Like

As a further tweak to my script, I’m trying to copy the contents of a note in Bear into a draft.

I’m unsure as to why the following script is not working:

let cb = CallbackURL.create();
    cb.baseURL = "bear://x-callback-url/open-note";
    cb.addParameter("title", 'Words');
    cb.waitForResponse = true;
    
    cb.open();
    
let response = context.callbackResponses[0];

if (response) {
  let result = response["note"];
  if (result) {
    draft.content(result);
    draft.update();}
}

This is a link to the Bear API relating to this parameter:

https://bear.app/faq/X-callback-url%20Scheme%20documentation/#open-note

Any pointers as to where I’m going wrong?

Thanks!

@martinperry Try this:

let cb = CallbackURL.create();
cb.baseURL = "bear://x-callback-url/open-note";
cb.addParameter("title", 'Words');
cb.waitForResponse = true;
    
if (cb.open()) {   
  let result = cb.callbackResponse.note;

  // if (result) {
    // This makes a new draft:
    let d = Draft.create();
    d.content = result;
    d.update();
    editor.load(d);
    
    // This overwrites current draft in editor:
    // draft.content = result;
    // draft.update();
  // }
} else {
  alert('Bear Note not found')
}
1 Like

@RoyRogers Thanks a lot for your help - it works a treat!

1 Like