Action

create crosslinked project in Craft and Todoist

Posted by FlohGro, Last update 11 days ago

Create crosslinked Project (Todoist + Craft)

Created by @FlohGro (twitter)

  • this action will create a crosslinked project between Craft and Todoist
  • the project in todoist will be created and link to the document in Craft
  • the document in Craft will be created from a template Draft you can specify and link to the project in todoist
  • when running the action you will be see a prompt asking for the name of the project (the prompt will be prefilled with the current Drafts content but you can set any name without affecting the current Draft)
  • known “issue”: if you quickly open the link to the todoist project after the action prepended the link to the project to the Craft document, your todoist app may show an error telling you that the project could not be found. The reason for this is, that todoist needs to sync the created project to your app first (the project is created via the REST API). After the project was synced, the error won’t occur again.

Configuration

  • to make this action work, you need to configure it correctly.
  • project template draft
    • the action can be configured to use a template draft for all your project documents or just create an empty project note
    • don’t use a project template draft
      • if you don’t want to use a template and your project documents in Craft shall be empty (besides the link to the project in todoist) you can disable the template usage by editing the script step of this action. Navigate to line 3 where the variable const templateDraftAvailable is set to true. Simply replace true with false and your project documents will be empty
    • configure a project template draft
      • if you want to use a template note create a new draft and give it a title so you can identify it. You can also add tags if you want to. Then setup your template in markdown format
      • when you finished the template tap the „info“ button in the editor and then tap the button titled „UUID“. This will copy the UUID of that draft.
      • then edit the script step of this action and find line 5 const templateDraftUUID = "unconfigured“; select the word unconfigured and paste the UUID you just copied (attention: don’t delete the quotation marks)
      • finally make sure that the variable const templateDraftAvailable in line 3 is set to true
  • craft spaceId
    • when you run the action the first time you will se a “Credential Prompt” which will ask you to insert your spaceId of Craft (this is neiden to create or open documents in your space).
      • to retrieve your spaceId just copy the deeplink of andy documenta in that space (refer to the Craft Support Page when you don’t know how to do that)
      • paste the copied id into a draft and you will se a link similar to this: “craftdocs://open?blockId=[the block id]&spaceId=[the spaceId]” - find the character combination “[the spaceId]” after the “spaceId=” and copy it
      • the run the action the first time and paste your spaceId into the Prompt.
      • this will store the spaceId in a Credential in Drafts and sync to your other connected devices - it is a one time action

Using the Action

  • using this action after the configuration is done is pretty simple. Just trigger it from whatever draft you currently opened.
  • the prompt will appear and you can use the content of the current draft as the project name or just type a new name.
  • the action will then create the project in todoist, retrieve its ID and create a new craft document (with the template you may have configured) linking to that project and then create an uncompletable task in the newly created project in todoist with a link to the craft document.

If you find this useful you can Buy Me A Coffee

Steps

  • script

    // is there an additional Draft Template which shall be added as content to the note?
    // set this to true if you want to use a template draft / if you don't want to use a template, then set it to false (no "" around the true / false)
    const templateDraftAvailable = true;
    // copy the uuid of your template draft for project documents and paste it between the quotation marks
    const templateDraftUUID = "unconfigured";
    
    // ------------------------------------------------------------------------
    // END OF CONFIGURATION
    // ANY CHANGES BELOW CAN LET THE ACTION FAIL - ONLY CHANGE THINGS IF YOU KNOW WHAT YOU ARE DOING
    // ------------------------------------------------------------------------
    
    
    
    if (templateDraftAvailable == true && templateDraftUUID == "unconfigured") {
        let errStr = "template Draft should be used but uuid is unconfigured, read the description of the action and configure it correctly."
        context.fail(errStr);
        app.displayErrorMessage("invalid configuration");
    } else {
    
        var spaceId = getCraftSpaceIdToUse();
        if (spaceId) {
    
            // create Todoist object
            var todoist = Todoist.create();
    
            // define project name
            let newProjectName = setProjectName()
    
            if (newProjectName) {
    
                let createdProject = createTodoistProject(newProjectName);
    
                if (createdProject) {
                    let projectLinkStringToDocument = createCraftDocumentAndLinkedprojectString(newProjectName, createdProject["id"])
                    if (projectLinkStringToDocument) {
                        createTaskInTodoistProject(projectLinkStringToDocument, createdProject["id"])
                    }
                }
            } else {
                // do error handling
                context.cancel("no project name set");
                app.displayWarningMessage("aborted; didn't select a project name");
            }
    
    
        } else {
            // do error handling
            context.cancel("Craft spaceId is not configured");
            app.displayWarningMessage("Craft spaceId is not configured");
        }
    }
    // ------------------------------------------------------------------------
    // FUNCTIONS
    // ------------------------------------------------------------------------
    
    
    function setProjectName() {
        let projectName = "";
        if (draft.content.length != 0) {
            projectName = draft.content;
        }
        var p = Prompt.create();
        p.title = "Project Name";
    
        p.addTextField("userProjectName", "", projectName, {
            wantsFocus: true
        });
    
        p.addButton("Set Project Name");
    
        if (p.show()) {
    
            projectName = p.fieldValues["userProjectName"]
            return projectName
        } else {
            return false
        }
    }
    
    
    function createTodoistProject(projectName) {
    
        // create the project in todoist
        let createProjectResponse = todoist.createProject({
            "name": projectName
        })
    
        if (!createProjectResponse) {
            let message = "Failed to add project to todoist: " + todoist.lastError;
            console.log(message)
            context.fail(message);
            app.displayErrorMessage(message);
        } else {
            let message = "successfully added project in todoist with name: " + projectName;
            console.log(message)
            // return relevant parameters of the
            return {
                "id": createProjectResponse["id"],
                "url": createProjectResponse["url"]
            }
        }
    }
    
    function createCraftDocumentAndLinkedprojectString(projectName, todoistProjectID) {
    
        const craftCreateBaseURL = "craftdocs://x-callback-url/createdocument?";
    
        const todoistShowProjectUrl = "todoist://project?id=" + todoistProjectID;
    
        const projectLinkText = "> Projekt in Todoist: [" + projectName + "](" + todoistShowProjectUrl + ")";
        let unencodedTitle = projectName;
        let title = encodeURIComponent(projectName);
    
        let templateContent = "";
        if (templateDraftAvailable) {
            let templateDraft = Draft.find(templateDraftUUID);
            templateContent = templateDraft.processTemplate("[[body]]")
            templateContent = "\n" + templateContent
        }
    
        let content = encodeURIComponent(projectLinkText + templateContent);
    
        var cbCreateCraftNote = CallbackURL.create()
        cbCreateCraftNote.baseURL = craftCreateBaseURL
        cbCreateCraftNote.addParameter("spaceId", spaceId)
        cbCreateCraftNote.addParameter("title", title)
        cbCreateCraftNote.addParameter("content", content)
        cbCreateCraftNote.addParameter("folderId", "")
        cbCreateCraftNote.waitForResponse = true
    
        let craftCreateReturn = cbCreateCraftNote.open()
    
        if (craftCreateReturn == true) {
            console.log("Craft note successfully created")
            let craftCreateResult = cbCreateCraftNote.callbackResponse
            var craftNoteLink = craftCreateResult.link
    
            let projectNoteStr = "Craft Note: " + craftNoteLink
            let projectTitle = "* **Craft Document: [" + unencodedTitle + "](" + craftNoteLink + ")**"
            return projectTitle;
    
        } else {
            console.log("Craft note result:" + cb.status + " " + cb.callbackResponse)
            if (cb.status == "cancelled") {
                context.cancel()
            } else {
                context.fail()
            }
            return
        }
    
    }
    
    function createTaskInTodoistProject(projectContent, projectId) {
        // create the task in todoist
        let createprojectResponse = todoist.createTask({
            "content": projectContent,
            "project_id": projectId
        })
    
        if (!createprojectResponse) {
            let message = "Failed to add project to todoist: " + todoist.lastError;
            console.log(message)
            context.fail(message);
            app.displayErrorMessage(message);
            return false;
        } else {
            let message = "successfully added project in todoist with content: " + projectContent + "to project with id: " + projectId;
            console.log(message)
            // return relevant parameters of the
            return true;
        }
    }
    
    function getCraftSpaceIdToUse() {
        let credential = Credential.create("CraftDocumentSpace", "Credential to store the spaceId of the space you want to use in Drafts.\ninsert your spaceId into the TextField below. \n\nNOTES: \n- this is a one time action, you don't need to do it everytime\n- Multiple spaces are currently not supported with this spaceId credential helper");
        credential.addTextField("spaceId", "spaceId");
        if (credential.authorize()) {
            return credential.getValue("spaceId");
        } else {
            let errorStr = "failed storing / retrieving space Id with credential"
            app.displayErrorMessage(errorStr);
            context.fail(errorStr);
            console.log(errorStr);
            return false;
        }
    }

Options

  • After Success Default
    Notification Info
    Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.