TaskRatchet bookmarklets

This thread is about bookmarklets that work with TaskRatchet. I’ll post my own and I’d love to see other people adding theirs or improving mine! So far I have only one, but I’m hoping to make at least an improved version of it in future.

Firstly, for anyone who doesn’t know:

Secondly, be aware that I’m not highly experienced with JavaScript and don’t like it much, so we may find that my bookmarklets don’t work in all cases. They may also not work in all browsers. I’ll be testing them only on Firefox on Linux. Anyone is welcome to suggest improvements to my code!

To make a bookmarklet from any code in this thread:

  1. Use your browser’s bookmarks manager to create a new, empty bookmark.
  2. Give it a name - anything you like.
  3. Optionally give it a keyword if your browser allows that (this means you could type the keyword into your browser’s address bar to execute the bookmarklet).
  4. Copy the code from a post below and paste it into the URL field.
  5. Edit the code as described in the post. You may actually wish to edit it in Notepad or similar then paste from there to the bookmark’s URL field.
  6. Save it.
  7. Move the bookmarklet to any bookmark folder and/or add tags if you use them. I like to have my bookmarklets on the bookmark toolbar.
  8. For each bookmarklet, you can create multiple versions if you wish to, each with different settings. I.e., create a new bookmark for each one and edit each one as needed.

This screenshot shows a bookmarklet that’s ready to be saved:

EDIT 2022-04-25:
Be aware that TaskRatchet’s API is not finalised and will likely change in future. Any bookmarklets that use the API would then stop working until they were updated. Updating them should be easy though.

3 Likes

This bookmarklet will create a task with a description that’s hard-coded into it – i.e., the same task name every time you use the bookmarklet.
The task will be due a specific number of minutes into the future – the same number of minutes every time you use the bookmarklet (but those minutes are always add on to the time at which you use the bookmarklet).

I use it to create an “emergency” task that makes me do a small amount of work immediately, to get me out of a state of frozen overwhelm, as described at Rod's Beeminder Journal - #4 by alys and in the comments below it (one of them includes a bash script version of this). You could use it if you often create the same task in TaskRatchet. You could make as many of these bookmarklets as you wanted, each with their own task name and number of minutes.

The bookmarklet uses TaskRatchet’s API so you have to get your User ID and API Token from the account page.

AN IMPORTANT NOTE: This bookmarklet will work only if you use it while you are looking at the TaskRatchet website. That’s because of the Cross-Origin Resource Sharing (CORS) security mechanism. I think there are ways to work around that, based on what I’ve been reading, so I’m hoping to make a new version that can be used no matter what site you’re looking at, but I don’t know when/if I’ll do that. :slight_smile:

How to edit the code:

  1. See the instructions in the top post of this thread.
  2. Paste your TaskRatchet API User ID where it says ‘ADD_USERID_HERE’. You must keep the single-quotes around the value.
  3. Paste your TaskRatchet API Token where it says ‘ADD_TOKEN_HERE’. You must keep the single-quotes around the value.
  4. Put whatever text you like where it says ‘PUT YOUR TASK NAME HERE’. You must keep the single-quotes around the value. Don’t use any single quotes as part of the text!
  5. Find where it says minutes_in_future=15; and change 15 to whatever number of minutes you want (e.g., 120 for 2 hours). You do not need to put single-quotes around it. You do need to keep the equals sign and semi-colon in place.
  6. If you want to customise the amount you’ll pay if you don’t complete the task on time, find where it says 'cents':500 - that’s $5. Change 500 to whatever you want. You do not need to put single-quotes around it. You do need to keep the colon in front of it (colon, not semi-colon).

When you use the bookmarklet, you’ll see a popup saying whether it was successful (“task created”) or not (“ERROR. Task NOT created”).

Post here if you have questions, comments, or improvements, or if any of this is confusing!

EDIT 2022-04-26: See the more convenient version below by @coolhandlouis!

This is the code to copy:
javascript:(function(){ task='PUT YOUR TASK NAME HERE'; minutes_in_future=15; datetime=new Date(+new Date() + 60000*minutes_in_future).toLocaleString('en-US').replace(/:[0-9][0-9] /g,' '); fetch('https://api.taskratchet.com/api1/me/tasks', { method:'POST', headers:{ 'X-Taskratchet-Userid':'ADD_USERID_HERE', 'X-Taskratchet-Token':'ADD_TOKEN_HERE', 'Content-Type':'application/json' }, body: JSON.stringify({'task':task, 'due':datetime, 'cents':500 }) }).then((response) => { if (response.ok) { alert('task created:\n' +task+' \n '+datetime ); } else { alert('ERROR. Task NOT created:\n' +task+' \n '+datetime ); } }) })();

4 Likes

This is fantastic! Thank you for sharing!

Obligatory note that the API is not finalized and will likely change in the future.

2 Likes

Here’s a new version of the bookmarklet above that creates a task with a hard-coded description.

This version can be used on any website. If you use it while not on the TaskRatchet site, you need to click it twice: the first time takes you to the site, and the second time creates the task. If you are already on the TaskRatchet site when you decide to use the bookmarklet, you need to click it only once (clicking it twice would create two tasks).

EDIT 2022-04-26: See the more convenient version below by @coolhandlouis!

This is the code to copy:
javascript:(function(){ task='PUT YOUR TASK NAME HERE'; minutes_in_future=15; L='https://app.taskratchet.com/'; if(location!=L) {location=L} else { datetime=new Date(+new Date() + 60000*minutes_in_future).toLocaleString('en-US').replace(/:[0-9][0-9] /g,' '); fetch('https://api.taskratchet.com/api1/me/tasks', { method:'POST', headers:{ 'X-Taskratchet-Userid':'ADD_USERID_HERE', 'X-Taskratchet-Token':'ADD_TOKEN_HERE', 'Content-Type':'application/json' }, body: JSON.stringify({'task':task, 'due':datetime, 'cents':500 }) }).then((response) => { if (response.ok) { alert('task created:\n'+task+'\n'+datetime); } else { alert('ERROR. Task NOT created:\n' +task+'\n'+datetime ); } }) }})();

Instructions for installing it are the same as for the first version above.


It should be possible to create a version of this that needs to be clicked only once, no matter what site you are on, but it would involve the use of JavaScript’s setTimeout and a Promise, and async code comes with a high probability of me throwing my laptop through a wall while screaming “Just execute things IN ORDER like a REAL language!!!”

5 Likes

Worked great on chrome, thanks! Here’s my very modest change, I put all of the entered data up front. Also, on my mac/chrome, I was able to just copy/paste this version with indents and line-feeds.

javascript: (function() {
    task = 'PUT YOUR TASK NAME HERE';
    minutes_in_future = 15;
    dollars = 5;
    USERID = 'ADD_USERID_HERE';
    TOKEN = 'ADD_TOKEN_HERE';
  
    L = 'https://app.taskratchet.com/';
    if (location != L) {
        location = L
    } else {
        datetime = new Date(+new Date() + 60000 * minutes_in_future).toLocaleString('en-US').replace(/:[0-9][0-9] /g, ' ');
        fetch('https://api.taskratchet.com/api1/me/tasks', {
            method: 'POST',
            headers: {
                'X-Taskratchet-Userid': USERID,
                'X-Taskratchet-Token': TOKEN,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                'task': task,
                'due': datetime,
                'cents': dollars * 100
            })
        }).then((response) => {
            if (response.ok) {
                alert('task created:\n' + task + '\n' + datetime);
            } else {
                alert('ERROR. Task NOT created:\n' + task + '\n' + datetime);
            }
        })
    }
})();
3 Likes

@coolhandlouis That’s much nicer! Thank you!

2 Likes

I’m confused about something–I checked, and I’ve already set CORS to be pretty permissive on the API. So I tested the original bookmarklet and it did indeed work on a different tab from TaskRatchet. So maybe the error you were seeing was for a different reason… ?

Here’s a version that prompts you for the different task attributes:

javascript:(function() {
    task = prompt('task','PUT YOUR TASK NAME HERE');
    minutes_in_future = prompt('minutes',15);
    dollars = prompt('dollars',5);
    USERID = 'ADD_USERID_HERE';
    TOKEN = 'ADD_TOKEN_HERE';

    datetime = new Date(+new Date() + 60000 * minutes_in_future).toLocaleString('en-US').replace(/:[0-9][0-9] /g, ' ');
    fetch('https://api.taskratchet.com/api1/me/tasks', {
        method: 'POST',
        headers: {
            'X-Taskratchet-Userid': USERID,
            'X-Taskratchet-Token': TOKEN,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            'task': task,
            'due': datetime,
            'cents': dollars * 100
        })
    }).then((response) => {
        if (response.ok) {
            alert('task created:\n' + task + '\n' + datetime);
        } else {
            alert('ERROR. Task NOT created:\n' + task + '\n' + datetime);
        }
    })
})();
2 Likes

I believe it will depend on the site that you are on when you run the bookmarklet. For example, I just tested my original version while on this forum site and it worked. It won’t work if I run it from twitter.com and this message is in the console:
Content Security Policy: The page’s settings blocked the loading of a resource at https://api.taskratchet.com/api1/me/tasks (“connect-src”).

If I understand the situation correctly, that’s not something you can fix, it’s from Twitter’s own security settings and so can’t be worked around without an intermediate step of going to TaskRatchet’s site before trying to connect to the API.

1 Like

Can you say more about how you’re thinking this would work? Thinking about giving it a go if I can understand better…

The sequence would be:

  1. Use “location = …” or similar to start loading the TaskRatchet website.
  2. Use the setTimeout() method to wait half a second or so for at least some of the site to load.
  3. Run the API command.

But the issue is that setTimeout is asynchronous so steps 2 and 3 will run more or less simultaneously, so the API command might be executed while the browser is still on the previous site. You could avoid that problem by putting a Promise around setTimeout, and doing step 3 only after the Promise has resolved, using the then() method or something. Async code annoys me so I haven’t done much with it ever.

There might also be DOM document load methods to check if the TaskRatchet site has loaded and only then do the API command. I’m not strong on that either.

1 Like

That is FANTASTIC, thanks! Also, big thanks to @alys! I’m so happy I have this set up now, this is REALLY going to help me. I put the insta-taskratchet bookmarklets (along with the custom taskratchet bookmarklet) in a folder on my bookmarks bar named $TaskRatchet$ to warn me about clicking in there. I might even put into a sub-sub folder, my cats like to walk on my keyboard, haha!

@narthur, you should definitely put that on github.

2 Likes

I… don’t think that approach will work, because once the location changes, the javascript stops executing. I wasn’t sure if that was the case for bookmarklets, but it seems like it is. Here’s a bookmarklet that waits half a second then says hello:

javascript:(()=>{
  setTimeout(()=>{ window.alert('hello') }, 500);
})();

that works just fine. But if we try to change the location first:

javascript:(()=>{
  window.location="https://example.com";
  setTimeout(()=>{ window.alert('hello') }, 500);
})();

The timeout never gets to fire, because the underlying page it was running on is gone.

1 Like

Yeah I read something about the location changing stopping the rest of the bookmarklet executing and a Promise was the way to work around that. I have obviously forgotten the fine details. The Promise may have been around both the location change and the setTimeout.