dvargas92495 / SmartBlocks

Useful examples from developer community for Roam42 SmartBlocks
147 stars 7 forks source link

Todoist: pulling in active tasks into Roam (Demonstrates how to bring back multiple blocks from API) #37

Open TfTHacker opened 3 years ago

TfTHacker commented 3 years ago

βœ‚οΈ Copy of your #42SmartBlock from Roam

πŸ“‹ Describe the SmartBlock

Queries Todist and pulls back active tasks as blocks, linked to Todoist.

βœ… Describe any prerequisites or dependencies that are required for this SmartBlock

You need to get your own API key and insert into the Javascript where noted in the code. Your API code for is at: https://todoist.com/prefs/integrations

πŸ“· Screenshot of your #42SmartBlock workflow/template from Roam

image

mlava commented 3 years ago

This is awesome. Thanks for sharing.

I've just started playing in the Todoist API and found you can query by filter. Here is an example - Today's Tasks only:

#42SmartBlock Todoist - Today
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; var url = "https://api.todoist.com/rest/v1/tasks?filter=Today"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); return '';
    %>

Happily playing with new SmartBlocks!!

Here are some more:

Inbox:

#42SmartBlock Todoist - Inbox
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; var url = "https://api.todoist.com/rest/v1/tasks?project_id=<10digitInboxID>"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); return '';
    %>

Today|Overdue:

#42SmartBlock Todoist - Today / Overdue
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; var url = "https://api.todoist.com/rest/v1/tasks?filter=Today|Overdue"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); return '';
    %>

Tomorrow:

#42SmartBlock Todoist - Tomorrow
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; var url = "https://api.todoist.com/rest/v1/tasks?filter=Tomorrow"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); return '';
    %>

One Project only:

#42SmartBlock Todoist - Project
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; var url = "https://api.todoist.com/rest/v1/tasks?project_id=<10digitProjectID>"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); return '';
    %>

Project ID or Inbox ID can be found by going to the web version of Todoist and opening your project or inbox. The ID is a ten digit string that comes after: https://todoist.com/app/#project%2F πŸ‘

mlava commented 3 years ago

Here's something else I've been playing with related to the example provided... checking off a task in Todoist if the check box is checked in Roam Research...

#42SmartBlock Todoist - tc (Task Complete)
    <%JAVASCRIPTASYNC:javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res = text.match(/\d{10}/);
    var myToken = 'your api token here'; 
    var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
    var bearer = 'Bearer ' + myToken; 
    var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); 
    return '';
    %>

I trigger this using the roamjs.com todo-trigger script, configured as below:

Append Text:: __(Completed at /Current Time)__ ;;tc

When I click the TODO checkbox in Roam, the todo-trigger appends that text to my block. The ;;tc launches the SmartBlock modal and I select the right SmartBlock and then it sends the POST to Todoist to mark it complete.

Does anyone know if it is possible to launch a SmartBlock directly with text, rather than having to select from the modal? That would complete my hack and make it perfect for my use... πŸ‘

@roamhacker


Edit / Update:

so, I forgot to account for TODOs that are Roam native and not linked to Todoist tasks! πŸ™„

I have worked around this by adding a #42Todoist tag to tasks pulled in from Todoist. Then, when completing the task the script checks for that tag and only contacts Todoist if it's a todoist task not a Roam TODO.

See modifications below -

for example, pull Inbox tasks:

#42SmartBlock Todoist - Inbox
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; 
    var url = "https://api.todoist.com/rest/v1/tasks?project_id=<10digitInboxID>"; 
    var bearer = 'Bearer ' + myToken; 
    var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" ); 
    return '';
    %>

You would need to add the #42Todoist to each SmartBlock version you create, in the var myTasks line as shown in my Inbox version above. i.e. if you want a today one, you need to include the #42Todoist part in my codes from prior post to ensure the tag is added and the task complete code will run.

For task complete:

#42SmartBlock Todoist - tc (Task Complete)
    <%JAVASCRIPTASYNC:javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res1 = text.match(/#42Todoist/);
    if (res1 !== null) {
        var res = text.match(/\d{10}/);
        var myToken = '2ecc8e5bba5706203a3f305a736735873252261c'; 
        var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
        var bearer = 'Bearer ' + myToken; 
        var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); 
       return ''; 
    } else {
       return'';
    }
    %>

If you're finicky and don't want to see the #42Todoist tag appended to all of your Todoist tasks, use this css in your roam/css file:

[data-tag="42Todoist"] {
  display: none !important;
}

Also, I changed the roamjs.com todo-trigger script to have two spaces before ;;tc as this makes the italics still work. Configured as below:

Append Text:: __(Completed at /Current Time)__ ;;tc

mlava commented 3 years ago

I'm spamming this thread, but this is a good one: creating Todoist tasks from Roam Research blocks πŸ‘

#42SmartBlock Todoist - Create new Task
    <%JAVASCRIPTASYNC: ```javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res1 = text.match(/#42Todoist/);
    if (res1 == null) {
        var raw = JSON.stringify({"content":""+text+""});
        var myToken = '<your api token here>';
        var url = "https://api.todoist.com/rest/v1/tasks";
        var bearer = 'Bearer ' + myToken;
        var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { "Content-Type":Β "application/json", Authorization: bearer }, data:raw, }).responseText; 
        var task = JSON.parse(myTasks);
        await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" );
        return '';
    } else {
        alert("This task is already a Todoist task");
        return '';
    }
    %>

This video shows this in use.

Basically, it checks if it is a Todoist task from the block text and if it is, it doesn't create a new one. If it isn't, it creates a new task in the Inbox and changes the Roam Research block to a TODO with link to the Todoist task. Of course, checking it as done can still launch the task complete SmartBlock as above to mark it off in Todoist.

This is pretty neat! πŸ‘

eatondpe commented 3 years ago

@mlava This is great. But I'm having an interesting problem with Today|Overdue. Specifically, nothing allows this "test" task to be pulled. Any thoughts? (Neither filter=today nor filter=overdue works either. But I have another task due tomorrow that pulls in just fine with filter=tomorrow. Weird.) image

filter=due before: today works, though.

billpetro commented 3 years ago

I'm spamming this thread, but this is a good one: creating Todoist tasks from Roam Research blocks πŸ‘

#42SmartBlock Todoist - Create new Task
    <%JAVASCRIPTASYNC: ```javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res1 = text.match(/#42Todoist/);
    if (res1 == null) {
        var raw = JSON.stringify({"content":""+text+""});
        var myToken = '<your api token here>';
        var url = "https://api.todoist.com/rest/v1/tasks";
        var bearer = 'Bearer ' + myToken;
        var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { "Content-Type":Β "application/json", Authorization: bearer }, data:raw, }).responseText; 
        var task = JSON.parse(myTasks);
        await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" );
        return '';
    } else {
        alert("This task is already a Todoist task");
        return '';
    }
    %>

This video shows this in use.

Basically, it checks if it is a Todoist task from the block text and if it is, it doesn't create a new one. If it isn't, it creates a new task in the Inbox and changes the Roam Research block to a TODO with link to the Todoist task. Of course, checking it as done can still launch the task complete SmartBlock as above to mark it off in Todoist.

This is pretty neat! πŸ‘

Mark,

I love what you've done with SmartBlocks, especially your bi-directional linking to Todoist!

Question: you talk about using a roamjs.com extension as part of your triggering mechanism. Can you provide a link, and how you incorporate it into your two SmartBlock scripts:

Thanks, Bill

eatondpe commented 3 years ago

Here you go, Bill.

https://roamjs.com/docs/extensions/todo-trigger/


From: Bill Petro notifications@github.com Sent: Tuesday, December 1, 2020 12:08 PM To: roamhacker/SmartBlocks SmartBlocks@noreply.github.com Cc: eatondpe eatondpe@outlook.com; Comment comment@noreply.github.com Subject: Re: [roamhacker/SmartBlocks] Todoist: pulling in active tasks into Roam (Demonstrates how to bring back multiple blocks from API) (#37)

I'm spamming this thread, but this is a good one: creating Todoist tasks from Roam Research blocks πŸ‘

42SmartBlock Todoist - Create new Task

<%JAVASCRIPTASYNC: ```javascript

var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;

var res1 = text.match(/#42Todoist/);

if (res1 == null) {

    var raw = JSON.stringify({"content":""+text+""});

    var myToken = '<your api token here>';

    var url = "https://api.todoist.com/rest/v1/tasks";

    var bearer = 'Bearer ' + myToken;

    var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { "Content-Type": "application/json", Authorization: bearer }, data:raw, }).responseText;

    var task = JSON.parse(myTasks);

    await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" );

    return '';

} else {

    alert("This task is already a Todoist task");

    return '';

}

%>

This videohttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.loom.com%2Fshare%2F7933a5221a9e4163986f23e080f99566&data=04%7C01%7C%7C809b4c2e21534d97049508d8961bbef4%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424393192148330%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=4jbJHBR1ef1HDu33XaAhZKRgbCweVwQS2V7IO9q64MY%3D&reserved=0 shows this in use.

Basically, it checks if it is a Todoist task from the block text and if it is, it doesn't create a new one. If it isn't, it creates a new task in the Inbox and changes the Roam Research block to a TODO with link to the Todoist task. Of course, checking it as done can still launch the task complete SmartBlock as above to mark it off in Todoist.

This is pretty neat! πŸ‘

Mark,

I love what you've done with SmartBlocks, especially your bi-directional linking to Todoist!

Question: you talk about using a roamjs.com extension as part of your triggering mechanism. Can you provide a link, and how you incorporate it into your two SmartBlock scripts:

Thanks, Bill

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37%23issuecomment-736689534&data=04%7C01%7C%7C809b4c2e21534d97049508d8961bbef4%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424393192158323%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=OlJan0Ujw4JlilEPD8rjPlLUNXdz7z0rulJ60dLyrYw%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAQCNZR2PQUEMAZYXARRZWF3SSUPJLANCNFSM4UF6AKKA&data=04%7C01%7C%7C809b4c2e21534d97049508d8961bbef4%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424393192168324%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=TW4xszyvICpYCfig9driKEoPhCOW7%2BJ6hoPT3OpZ7oM%3D&reserved=0.

billpetro commented 3 years ago

https://roamjs.com/docs/extensions/todo-trigger/

Thanks, David!

I've copied that code into my roam/js and enabled it.

Running: ;;Todoist - Today will populate my Todoist tasks into Roam, but clicking them DONE will not sync back to Todoist.

Similarly, running: Todoist - tc (Task Complete) yields no joy

Even: Todoist - Create new Task doesn't work

Might it be because @Roamhacker took down part of 24moar?

Thanks, Bill

eatondpe commented 3 years ago

I haven't tried the Roam-->Todoist path yet, myself. But it's on my list.

I'm still traveling, which makes testing a bit challenging.


From: Bill Petro notifications@github.com Sent: Tuesday, December 1, 2020 3:11 PM To: roamhacker/SmartBlocks SmartBlocks@noreply.github.com Cc: eatondpe eatondpe@outlook.com; Comment comment@noreply.github.com Subject: Re: [roamhacker/SmartBlocks] Todoist: pulling in active tasks into Roam (Demonstrates how to bring back multiple blocks from API) (#37)

https://roamjs.com/docs/extensions/todo-trigger/https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Froamjs.com%2Fdocs%2Fextensions%2Ftodo-trigger%2F&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158792510%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=2BeLkFgarqOrdbAuO%2FU2w1Rt%2F8%2BlktSJqLTPiapNLmc%3D&reserved=0

Thanks, David!

I've copied that code into my roam/js and enabled it.

Running: ;;Todoist - Today will populate my Todoist tasks into Roam, but clicking them DONE will not sync back to Todoist.

Similarly, running: Todoist - tc (Task Complete) yields no joy

Even: Todoist - Create new Task doesn't work

Might it be because @roamhackerhttps://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158792510%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=6rEGPeotk2bPluBLTrqCKckVkizq2SfhCZm4vczNQp8%3D&reserved=0 took down part of 24moar?

Thanks, Bill

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37%23issuecomment-736790588&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158802506%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=jNm6DnNbO9nCxrP4lQsd2fE6dhN9uQsFgMYhzgczSjI%3D&reserved=0, or unsubscribehttps://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAQCNZRYX7ASS43L3LAMCPTTSSVEYVANCNFSM4UF6AKKA&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158802506%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=edxLFGAkott7uG%2FZrLqapaYbIl%2BVv3K%2FDWVlr%2FC5Bjk%3D&reserved=0.

billpetro commented 3 years ago

I haven't tried the Roam-->Todoist path yet, myself. But it's on my list. I'm still traveling, which makes testing a bit challenging.

Thanks David. It is addicting, isn't it?

eatondpe commented 3 years ago

Yes, sir. It is! And I love it. SmartBlocks is changing everything for me.


From: Bill Petro notifications@github.com Sent: Tuesday, December 1, 2020 3:28 PM To: roamhacker/SmartBlocks SmartBlocks@noreply.github.com Cc: eatondpe eatondpe@outlook.com; Comment comment@noreply.github.com Subject: Re: [roamhacker/SmartBlocks] Todoist: pulling in active tasks into Roam (Demonstrates how to bring back multiple blocks from API) (#37)

I haven't tried the Roam-->Todoist path yet, myself. But it's on my list. I'm still traveling, which makes testing a bit challenging. … ____ From: Bill Petro notifications@github.commailto:notifications@github.com Sent: Tuesday, December 1, 2020 3:11 PM To: roamhacker/SmartBlocks SmartBlocks@noreply.github.commailto:SmartBlocks@noreply.github.com Cc: eatondpe eatondpe@outlook.commailto:eatondpe@outlook.com; Comment comment@noreply.github.commailto:comment@noreply.github.com Subject: Re: [roamhacker/SmartBlocks] Todoist: pulling in active tasks into Roam (Demonstrates how to bring back multiple blocks from API) (#37https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346877240%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=ylzHKQxqoOsHfHaMRZ2vTe36HWFX89OiQVN1PE0v3VU%3D&reserved=0) https://roamjs.com/docs/extensions/todo-trigger/https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Froamjs.com%2Fdocs%2Fextensions%2Ftodo-trigger%2F&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346887238%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=7EoUx2qhkxtoGFpbA4%2BBU4Zv3BSpiECR7W75x42%2BhKs%3D&reserved=0https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Froamjs.com%2Fdocs%2Fextensions%2Ftodo-trigger%2F&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158792510%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=2BeLkFgarqOrdbAuO%2FU2w1Rt%2F8%2BlktSJqLTPiapNLmc%3D&reserved=0https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Froamjs.com%2Fdocs%2Fextensions%2Ftodo-trigger%2F&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346897234%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=59ZIHm3vyaOEBVICcHcos%2Bh2aRxNtVE2OE1oTfM%2FzpA%3D&reserved=0 Thanks, David! I've copied that code into my roam/js and enabled it. Running: ;;Todoist - Today will populate my Todoist tasks into Roam, but clicking them DONE will not sync back to Todoist. Similarly, running: Todoist - tc (Task Complete) yields no joy Even: Todoist - Create new Task doesn't work Might it be because @roamhackerhttps://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346907226%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=XM8ogDl5oyrQiinPvIGV9oeZLm8t4uqQLKIpKnH4ft8%3D&reserved=0https://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158792510%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=6rEGPeotk2bPluBLTrqCKckVkizq2SfhCZm4vczNQp8%3D&reserved=0https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346917222%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=ipMd8LOXV4f3ANMB9qbbMYCEZdIuhhMk5yvIMbKhH1g%3D&reserved=0 took down part of 24moar? Thanks, Bill β€” You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37%23issuecomment-736790588&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158802506%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=jNm6DnNbO9nCxrP4lQsd2fE6dhN9uQsFgMYhzgczSjI%3D&reserved=0https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37%23issuecomment-736790588&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346927216%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=v8id%2FT6mOrmjQTMFkoZaVF4UI%2Bdi9RCcPzdwwNJn2kk%3D&reserved=0, or unsubscribehttps://nam10.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAQCNZRYX7ASS43L3LAMCPTTSSVEYVANCNFSM4UF6AKKA&data=04%7C01%7C%7Cd9f97117deac402f52a508d89635596c%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424503158802506%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=edxLFGAkott7uG%2FZrLqapaYbIl%2BVv3K%2FDWVlr%2FC5Bjk%3D&reserved=0https://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAQCNZRYX7ASS43L3LAMCPTTSSVEYVANCNFSM4UF6AKKA&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346937211%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=WEPD%2FUiZ9%2FmoEXYwl80su5KABu%2FZVuXh0VhRWeZ7NRY%3D&reserved=0.

Thanks David. It is addicting, isn't it?

β€” You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Froamhacker%2FSmartBlocks%2Fissues%2F37%23issuecomment-736800098&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346947214%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=moB1wT38jwhvq5RQsI3%2BIxFtvSQgN8s8CvHrBkJ4zco%3D&reserved=0, or unsubscribehttps://nam03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAQCNZR2PK64IRX5F4EXGO43SSVGYLANCNFSM4UF6AKKA&data=04%7C01%7C%7Cc806d064e1ba41cc042708d89637b8e7%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C637424513346957207%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=KpoyDQk77MKXdhWvQPUQ4%2FoRFvuCXEuY9v2VNM%2BUbRc%3D&reserved=0.

billpetro commented 3 years ago

If I could get these Todoist bi-directional SmartBlocks working, It would be a huge jump forward. I use Todoist almost as much as email. Could Roam SmartBlocks do email? ;-)

kerim commented 3 years ago

This is amazing. I can't figure out the two way sync yet, but I'm having trouble just using filters. If I do:

https://api.todoist.com/rest/v1/tasks?filter=p1

that works fine, but I can't do this:

https://api.todoist.com/rest/v1/tasks?filter=p1&!assigned to:others

I've tried escaping it using a URL encoder, but that doesn't seem to help.

https://api.todoist.com/rest/v1/tasks?filter=p1%26%21assigned%20to%3Aothers

Suggestions?

kerim commented 3 years ago

Another approach might be to create a filter in Todoist and just use the ID number for that filter, but I'm not sure on the syntax for this either...

kerim commented 3 years ago

OK. I think I was just encoding it wrong. This works: tasks?filter=p1%26!assigned%20to%3A%20others (encoding the & but not the !)

eatondpe commented 3 years ago

@kerim Thanks! That's very helpful.

kerim commented 3 years ago

Roam 42 now has new button triggers. I wonder if one could simply replace {{[[TODO]]}} with a button trigger for each of the Todoist tasks? Might be a little simpler?

kerim commented 3 years ago

I'm not very good at this. Can someone tell me why this isn't working?

This pulls my Priority 2 todos from Todoist and adds a button before each one. No problem so far.

var myToken = '[TOKEN]'; var url = "https://api.todoist.com/rest/v1/tasks?filter=p2%26!assigned%20to%3A%20others"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{->Done}} [" + task.content + "](" + task.url + ")" ); return '';

This should then run when someone clicks on the button, marking it as done in Todoist, but it isn't working for me...

var text = roam42.smartBlocks.activeWorkflow.startingBlockContents; var myToken = '[TOKEN]'; var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; var bearer = 'Bearer ' + myToken; var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{->Done}} [" + task.content + "](" + task.url + ")" ); return '';

Thanks!

mlava commented 3 years ago

New code for dated tasks. This one leverages the new DATEBASISDAILYNOTES so that you can run this code on any daily page and it will pull the tasks from Todoist for that date.

No more needing separate smartblocks for today, tomorrow etc!

#42SmartBlock test
    <%DATEBASISDAILYNOTES%>
    <%NOBLOCKOUTPUT%><%SET:date,<%DATE:today%>%>
    <%JAVASCRIPTASYNC:
    var date = roam42.smartBlocks.activeWorkflow.vars["date"];
    date = chrono.parseDate(date);
    date = formatDate(date);
    var myToken = '2ecc8e5bba5706203a3f305a736735873252261c'; 
    var url = "https://api.todoist.com/rest/v1/tasks?filter=" + date + "";
    var bearer = 'Bearer ' + myToken; 
    var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); 
    return '';

    function formatDate(date) {
        var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

        if (month.length < 2) month = '0' + month;
        if (day.length < 2) day = '0' + day;

        return [year, month, day].join('-');
    }
%>
rushikb commented 3 years ago

@mlava β€” these are amazing β€” qq, now that ;; doesn't really work for triggering smartblocksβ€” have you figured out a better way to trigger the todoist complete block?

TfTHacker commented 3 years ago

@mlava β€” these are amazing β€” qq, now that ;; doesn't really work for triggering smartblocksβ€” have you figured out a better way to trigger the todoist complete block?

The new trigger for SmartBlocks is jj (two j) - give it a try

mlava commented 3 years ago

Here are two SmartBlocks that allow use of Todoist inbox as Quick Capture mechanism for Roam Research:

Loom: https://www.loom.com/share/8e52ef9cdbc84d58adf9d2f2a48f432e

Code:

- #42SmartBlock Todoist - QC Inbox
    - <%JAVASCRIPTASYNC: ```javascript
var myToken = '<your API token here>';
var url = "https://api.todoist.com/rest/v1/tasks?project_id=<your inbox id>&label_id=<your RR label id>";
var bearer = 'Bearer ' + myToken;
var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 
//console.log(myTasks);
for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
return '';``` %>
- #42SmartBlock Todoist - QC Complete
    - <%JAVASCRIPTASYNC: ```javascript
var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
var res = text.match(/\d{10}/);
var myToken = '<your API token here>'; 
var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
var bearer = 'Bearer ' + myToken;

var settings = {
  "url": url,
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Authorization": bearer
  },
};

$.ajax(settings);

var doneText = text.replace("#[[Quick Capture]]", "");
const reg = /(#42TDQC_\d{10})/;
var doneText = doneText.replace(reg, "");
let txtarea = document.activeElement;
var setValue = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setValue.call(txtarea, doneText);
const inputEvent = new Event('input', { bubbles: true });
txtarea.dispatchEvent(inputEvent);```%><%NOBLOCKOUTPUT%>
mlava commented 3 years ago

New version for using Todoist inbox as quick capture for RR. Please see the previous post. This version has 3 smartblocks. One pulls from inbox if you have labelled for Roam Research (you need the label id for the smartblock). Second pulls in comments and pdf/jpg/png attachments if there are any. Third removes from todoist inbox and is unchanged from last post.

- #42SmartBlock Todoist - QC Inbox
    - <%JAVASCRIPTASYNC: ```javascript
var myToken = '<your API token here>';
var url = "https://api.todoist.com/rest/v1/tasks?project_id=<your inbox id here>&label_id=<your label id here>";
var bearer = 'Bearer ' + myToken;
var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 
console.log(myTasks);
for await (task of JSON.parse(myTasks)) {
  if (task.comment_count > 0) {
  roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
  roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{Get Comments:42SmartBlock:Todoist - QC Comments:taskId=" + task.id +"}}  " );
  }
  else {
    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
  }
}
return '';``` %>
- #42SmartBlock Todoist - QC Comments
    - <%JAVASCRIPTASYNC: ```javascript
let taskId = roam42.smartBlocks.activeWorkflow.vars["taskId"];
var myToken = '<your API token here>';
var url = "https://api.todoist.com/rest/v1/comments?task_id=" + taskId + "";
var bearer = 'Bearer ' + myToken;
var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 
console.log(myComments);

for await (comment of JSON.parse(myComments)) {
    console.log(comment);
    if (comment.hasOwnProperty('attachment')) {
      if (comment.attachment.file_type == "application/pdf") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
      } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
      } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "" );
        }
    } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "" );
    }
}
return '';``` %>
- #42SmartBlock Todoist - QC Complete
    - <%JAVASCRIPTASYNC: ```javascript
var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
var res = text.match(/\d{10}/);
var myToken = '<your API token here>'; 
var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
var bearer = 'Bearer ' + myToken;

var settings = {
  "url": url,
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Authorization": bearer
  },
};

$.ajax(settings);

var doneText = text.replace("#[[Quick Capture]]", "");
const reg = /(#42TDQC_\d{10})/;
var doneText = doneText.replace(reg, "");
let txtarea = document.activeElement;
var setValue = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setValue.call(txtarea, doneText);
const inputEvent = new Event('input', { bubbles: true });
txtarea.dispatchEvent(inputEvent);```%><%NOBLOCKOUTPUT%>
mlava commented 3 years ago

Ok.

Here is another version. This one allows for automatic checking off of tasks in todoist upon import into Roam Research, as well as automatic import of comments and attachments. There is also the option to use the manual workflow demonstrated earlier.

- #42SmartBlock Todoist - QC Inbox
    - <%NOBLOCKOUTPUT%><%SET:label,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:mode,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:comments,<%RESOLVEBLOCKREF:<block ref>%>%>
    - <%JAVASCRIPTASYNC: ```javascript
var myToken = '<your API key here>';
var url = "https://api.todoist.com/rest/v1/tasks?project_id=<your inbox id here>&label_id=" + roam42.smartBlocks.activeWorkflow.vars["label"] + "";
var bearer = 'Bearer ' + myToken;   
var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText;
var taskIDs = [];
for await (task of JSON.parse(myTasks)) {
    if (roam42.smartBlocks.activeWorkflow.vars["mode"] == "Manual") {
        if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Manual") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{Get Comments:42SmartBlock:Todoist - QC Comments:taskId=" + task.id +"}}  " );
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
            }
        } else if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Automatic") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );

                var url = "https://api.todoist.com/rest/v1/comments?task_id=" + task.id + "";
                var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 

                for await (comment of JSON.parse(myComments)) {
                    console.log(comment);
                    if (comment.hasOwnProperty('attachment')) {
                    if (comment.attachment.file_type == "application/pdf") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
                    } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                        }
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                    }
                }
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
            }
        } else {
            alert("Please enter either Manual or Automatic in the Comments settings");
            return'';
        }
    } else if (roam42.smartBlocks.activeWorkflow.vars["mode"] == "Automatic") {
        if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Manual") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{Get Comments:42SmartBlock:Todoist - QC Comments:taskId=" + task.id +"}}  " );
            } else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
            }
        } else if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Automatic") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
                var url = "https://api.todoist.com/rest/v1/comments?task_id=" + task.id + "";
                var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 

                for await (comment of JSON.parse(myComments)) {
                    console.log(comment);
                    if (comment.hasOwnProperty('attachment')) {
                    if (comment.attachment.file_type == "application/pdf") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
                    } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                        }
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                    }
                }
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
            }
        } else {
            alert("Please enter either Manual or Automatic in the Comments settings");
            return'';
        }
        var url = "https://api.todoist.com/rest/v1/tasks/"+task.id+"/close"; 
        var settings = {
            "url": url,
            "method": "POST",
            "timeout": 0,
            "headers": {
                "Authorization": bearer
            },
        };
        $.ajax(settings);
    } else {
        alert("Please enter either Manual or Automatic in the Mode settings");
        return'';
    }
}
return '';``` %>
- #42SmartBlock Todoist - QC Comments
    - <%JAVASCRIPTASYNC: ```javascript
let taskId = roam42.smartBlocks.activeWorkflow.vars["taskId"];
var myToken = '<your API key here>';
var url = "https://api.todoist.com/rest/v1/comments?task_id=" + taskId + "";
var bearer = 'Bearer ' + myToken;
var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 
console.log(myComments);

for await (comment of JSON.parse(myComments)) {
    console.log(comment);
    if (comment.hasOwnProperty('attachment')) {
      if (comment.attachment.file_type == "application/pdf") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
      } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
      } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
        }
    } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
    }
}
return '';``` %>
- #42SmartBlock Todoist - QC Complete
    - <%JAVASCRIPTASYNC: ```javascript
var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
var res = text.match(/\d{10}/);
var myToken = '<your API key here>'; 
var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
var bearer = 'Bearer ' + myToken;

var settings = {
  "url": url,
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Authorization": bearer
  },
};

$.ajax(settings);

var doneText = text.replace("#[[Quick Capture]]", "");
const reg = /(#42TDQC_\d{10})/;
var doneText = doneText.replace(reg, "");
let txtarea = document.activeElement;
var setValue = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setValue.call(txtarea, doneText);
const inputEvent = new Event('input', { bubbles: true });
txtarea.dispatchEvent(inputEvent);```%><%NOBLOCKOUTPUT%>

Note: this version requires you to link to block references to some settings:

- **Todoist Quick Capture preferences:**
    - **Todoist label id:**    __(10 digit number)__
        - 
    - **Preferred Completion mode:**    __(Manual | Automatic)__
        - 
    - **Comment / attachment handling:**    __(Manual | Automatic)__
        - 

Copy this block anywhere in your graph, although I have it on my Todoist SmartBlocks page down the bottom. Fill in your preferences, making sure to spell correctly. Then, copy the block references for each option and paste into the required spot in the first SmartBlock listed above.

i.e.

- #42SmartBlock Todoist - QC Inbox
    - <%NOBLOCKOUTPUT%><%SET:label,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:mode,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:comments,<%RESOLVEBLOCKREF:<block ref>%>%>

Replace three times with the block reference to that part of your settings.

Your settings should look like: image

And the first smartblock first section like: image

Download: Todoist QC SB.zip

skyperkloze commented 3 years ago

Great work! any plan to do with the OmniFocus?

cyu60 commented 3 years ago

Ok.

Here is another version. This one allows for automatic checking off of tasks in todoist upon import into Roam Research, as well as automatic import of comments and attachments. There is also the option to use the manual workflow demonstrated earlier.

- #42SmartBlock Todoist - QC Inbox
    - <%NOBLOCKOUTPUT%><%SET:label,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:mode,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:comments,<%RESOLVEBLOCKREF:<block ref>%>%>
    - <%JAVASCRIPTASYNC: ```javascript
var myToken = '<your API key here>';
var url = "https://api.todoist.com/rest/v1/tasks?project_id=<your inbox id here>&label_id=" + roam42.smartBlocks.activeWorkflow.vars["label"] + "";
var bearer = 'Bearer ' + myToken; 
var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText;
var taskIDs = [];
for await (task of JSON.parse(myTasks)) {
    if (roam42.smartBlocks.activeWorkflow.vars["mode"] == "Manual") {
        if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Manual") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{Get Comments:42SmartBlock:Todoist - QC Comments:taskId=" + task.id +"}}  " );
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
            }
        } else if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Automatic") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );

                var url = "https://api.todoist.com/rest/v1/comments?task_id=" + task.id + "";
                var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 

                for await (comment of JSON.parse(myComments)) {
                    console.log(comment);
                    if (comment.hasOwnProperty('attachment')) {
                    if (comment.attachment.file_type == "application/pdf") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
                    } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                        }
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                    }
                }
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]] #42TDQC_" + task.id +" {{Complete:42SmartBlock:Todoist - QC Complete}}  " );
            }
        } else {
            alert("Please enter either Manual or Automatic in the Comments settings");
            return'';
        }
    } else if (roam42.smartBlocks.activeWorkflow.vars["mode"] == "Automatic") {
        if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Manual") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{Get Comments:42SmartBlock:Todoist - QC Comments:taskId=" + task.id +"}}  " );
            } else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
            }
        } else if (roam42.smartBlocks.activeWorkflow.vars["comments"] == "Automatic") {
            if (task.comment_count > 0) {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
                var url = "https://api.todoist.com/rest/v1/comments?task_id=" + task.id + "";
                var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 

                for await (comment of JSON.parse(myComments)) {
                    console.log(comment);
                    if (comment.hasOwnProperty('attachment')) {
                    if (comment.attachment.file_type == "application/pdf") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
                    } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
                        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                        }
                    } else {
                    roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
                    }
                }
            }
            else {
                roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + task.content + " #[[Quick Capture]]  " );
            }
        } else {
            alert("Please enter either Manual or Automatic in the Comments settings");
            return'';
        }
        var url = "https://api.todoist.com/rest/v1/tasks/"+task.id+"/close"; 
        var settings = {
            "url": url,
            "method": "POST",
            "timeout": 0,
            "headers": {
                "Authorization": bearer
            },
        };
        $.ajax(settings);
    } else {
        alert("Please enter either Manual or Automatic in the Mode settings");
        return'';
    }
}
return '';``` %>
- #42SmartBlock Todoist - QC Comments
    - <%JAVASCRIPTASYNC: ```javascript
let taskId = roam42.smartBlocks.activeWorkflow.vars["taskId"];
var myToken = '<your API key here>';
var url = "https://api.todoist.com/rest/v1/comments?task_id=" + taskId + "";
var bearer = 'Bearer ' + myToken;
var myComments = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; 
console.log(myComments);

for await (comment of JSON.parse(myComments)) {
  console.log(comment);
      if (comment.hasOwnProperty('attachment')) {
      if (comment.attachment.file_type == "application/pdf") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{pdf: " + comment.attachment.file_url + "}}" );
      } else if (comment.attachment.file_type == "image/jpeg" || comment.attachment.file_type == "image/png") {
        roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "![](" + comment.attachment.file_url + ")" );
      } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
      }
    } else {
      roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "" + comment.content + "  " );
    }
}
return '';``` %>
- #42SmartBlock Todoist - QC Complete
    - <%JAVASCRIPTASYNC: ```javascript
var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
var res = text.match(/\d{10}/);
var myToken = '<your API key here>'; 
var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
var bearer = 'Bearer ' + myToken;

var settings = {
  "url": url,
  "method": "POST",
  "timeout": 0,
  "headers": {
    "Authorization": bearer
  },
};

$.ajax(settings);

var doneText = text.replace("#[[Quick Capture]]", "");
const reg = /(#42TDQC_\d{10})/;
var doneText = doneText.replace(reg, "");
let txtarea = document.activeElement;
var setValue = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setValue.call(txtarea, doneText);
const inputEvent = new Event('input', { bubbles: true });
txtarea.dispatchEvent(inputEvent);```%><%NOBLOCKOUTPUT%>

Note: this version requires you to link to block references to some settings:

- **Todoist Quick Capture preferences:**
    - **Todoist label id:**    __(10 digit number)__
        - 
    - **Preferred Completion mode:**    __(Manual | Automatic)__
        - 
    - **Comment / attachment handling:**    __(Manual | Automatic)__
        - 

Copy this block anywhere in your graph, although I have it on my Todoist SmartBlocks page down the bottom. Fill in your preferences, making sure to spell correctly. Then, copy the block references for each option and paste into the required spot in the first SmartBlock listed above.

i.e.

- #42SmartBlock Todoist - QC Inbox
    - <%NOBLOCKOUTPUT%><%SET:label,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:mode,<%RESOLVEBLOCKREF:<block ref>%>%><%SET:comments,<%RESOLVEBLOCKREF:<block ref>%>%>

Replace three times with the block reference to that part of your settings.

Your settings should look like: image

And the first smartblock first section like: image

Download: Todoist QC SB.zip

Would you mind kindly sharing another loom recording demonstrating the setup and differences? Thanks!

arash-ak commented 3 years ago

Fantastic Work. Extremely useful.

With comments, texts works as intended yet pdfs and images need to first be opened through the link, and then relinked, since todoist uses "cloudfront.net" for their files.

ajbarry2018 commented 3 years ago

Here's something else I've been playing with related to the example provided... checking off a task in Todoist if the check box is checked in Roam Research...

#42SmartBlock Todoist - tc (Task Complete)
    <%JAVASCRIPTASYNC:javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res = text.match(/\d{10}/);
    var myToken = 'your api token here'; 
    var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
    var bearer = 'Bearer ' + myToken; 
    var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); 
    return '';
    %>

I trigger this using the roamjs.com todo-trigger script, configured as below:

Append Text:: __(Completed at /Current Time)__ ;;tc

When I click the TODO checkbox in Roam, the todo-trigger appends that text to my block. The ;;tc launches the SmartBlock modal and I select the right SmartBlock and then it sends the POST to Todoist to mark it complete.

Does anyone know if it is possible to launch a SmartBlock directly with text, rather than having to select from the modal? That would complete my hack and make it perfect for my use... πŸ‘

@roamhacker

Edit / Update:

so, I forgot to account for TODOs that are Roam native and not linked to Todoist tasks! πŸ™„

I have worked around this by adding a #42Todoist tag to tasks pulled in from Todoist. Then, when completing the task the script checks for that tag and only contacts Todoist if it's a todoist task not a Roam TODO.

See modifications below -

for example, pull Inbox tasks:

#42SmartBlock Todoist - Inbox
    <%JAVASCRIPTASYNC:javascript
    var myToken = 'your api token here'; 
    var url = "https://api.todoist.com/rest/v1/tasks?project_id=<10digitInboxID>"; 
    var bearer = 'Bearer ' + myToken; 
    var myTasks = $.ajax({url:url, type:"GET", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" ); 
    return '';
    %>

You would need to add the #42Todoist to each SmartBlock version you create, in the var myTasks line as shown in my Inbox version above. i.e. if you want a today one, you need to include the #42Todoist part in my codes from prior post to ensure the tag is added and the task complete code will run.

For task complete:

#42SmartBlock Todoist - tc (Task Complete)
    <%JAVASCRIPTASYNC:javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res1 = text.match(/#42Todoist/);
    if (res1 !== null) {
        var res = text.match(/\d{10}/);
        var myToken = '2ecc8e5bba5706203a3f305a736735873252261c'; 
        var url = "https://api.todoist.com/rest/v1/tasks/"+res+"/close"; 
        var bearer = 'Bearer ' + myToken; 
        var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { Authorization: bearer }, }).responseText; for(task of JSON.parse(myTasks)) await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ")" ); 
       return ''; 
    } else {
       return'';
    }
    %>

If you're finicky and don't want to see the #42Todoist tag appended to all of your Todoist tasks, use this css in your roam/css file:

[data-tag="42Todoist"] {
  display: none !important;
}

Also, I changed the roamjs.com todo-trigger script to have two spaces before ;;tc as this makes the italics still work. Configured as below:

Append Text:: __(Completed at /Current Time)__ ;;tc

How do I do the Task complete command? Do I paste the 'Append Text:: (Completed at /Current Time) ;;tc` into the JS code page??

mlava commented 3 years ago

For all discussion of using Todoist Quick Capture for Roam, please use the new thread: https://github.com/roamhacker/SmartBlocks/issues/187

For the other scripts that are for task completion etc, continue on here.

mlava commented 3 years ago

Fantastic Work. Extremely useful.

With comments, texts works as intended yet pdfs and images need to first be opened through the link, and then relinked, since todoist uses "cloudfront.net" for their files.

Yep, thanks for reporting. Will have to switch off for Free users and just import text...

fglhelmink commented 3 years ago

I'm spamming this thread, but this is a good one: creating Todoist tasks from Roam Research blocks πŸ‘

#42SmartBlock Todoist - Create new Task
    <%JAVASCRIPTASYNC: ```javascript
    var text = roam42.smartBlocks.activeWorkflow.startingBlockContents;
    var res1 = text.match(/#42Todoist/);
    if (res1 == null) {
        var raw = JSON.stringify({"content":""+text+""});
        var myToken = '<your api token here>';
        var url = "https://api.todoist.com/rest/v1/tasks";
        var bearer = 'Bearer ' + myToken;
        var myTasks = $.ajax({url:url, type:"POST", async:false, headers: { "Content-Type":Β "application/json", Authorization: bearer }, data:raw, }).responseText; 
        var task = JSON.parse(myTasks);
        await roam42.smartBlocks.activeWorkflow.outputAdditionalBlock( "{{[[TODO]]}} [" + task.content + "](" + task.url + ") #42Todoist" );
        return '';
    } else {
        alert("This task is already a Todoist task");
        return '';
    }
    %>

This video shows this in use.

Basically, it checks if it is a Todoist task from the block text and if it is, it doesn't create a new one. If it isn't, it creates a new task in the Inbox and changes the Roam Research block to a TODO with link to the Todoist task. Of course, checking it as done can still launch the task complete SmartBlock as above to mark it off in Todoist.

This is pretty neat! πŸ‘

This is amazing! Thank you so much for all your work. Did you already work out the bug regarding the #42Todoi tag vs #42Todoist tag? Would love to streamline my tasks!