From be92ffca862219ef4a96470ce6f97545a95d39e4 Mon Sep 17 00:00:00 2001 From: "C. Thomas Bailey" Date: Wed, 4 Jan 2017 23:10:37 +0900 Subject: [PATCH] Quick, write that down. --- .gitignore | 4 + handle-action.js | 16 +++ icon-for.js | 6 + icons/readme.md | 1 + index.js | 8 ++ info.plist | 307 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 17 +++ readme.md | 29 +++++ show-actions.js | 29 +++++ show-cards.js | 14 +++ show-lists.js | 14 +++ trello.js | 57 +++++++++ 12 files changed, 502 insertions(+) create mode 100644 .gitignore create mode 100644 handle-action.js create mode 100644 icon-for.js create mode 100644 icons/readme.md create mode 100644 index.js create mode 100644 info.plist create mode 100644 package.json create mode 100644 readme.md create mode 100644 show-actions.js create mode 100644 show-cards.js create mode 100644 show-lists.js create mode 100644 trello.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6fc6ba5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +secrets.js +icons/* +!icons/readme.md +icon.png diff --git a/handle-action.js b/handle-action.js new file mode 100644 index 0000000..ffb4c9e --- /dev/null +++ b/handle-action.js @@ -0,0 +1,16 @@ +const alfy = require('alfy') +const open = require('open') +const notifier = require('node-notifier') +const { postCard } = require('./trello') + +const input = JSON.parse(alfy.input) + +const notify = (title, message) => notifier.notify({ title, message }) + +if (input.action === 'create') { + postCard(input.card) + .then(() => notify('It worked ⚡️', "Your card's now on Trello.")) + .catch(reason => notify('Oops 🌧', `Hit a problem: ${reason}`)) +} else if (input.action === 'show') { + open(input.url) +} diff --git a/icon-for.js b/icon-for.js new file mode 100644 index 0000000..788fd49 --- /dev/null +++ b/icon-for.js @@ -0,0 +1,6 @@ +module.exports = function iconFor (listName) { + const fileName = listName.toLowerCase().replace(' ', '_') + return { + path: `./icons/${fileName}.png` + } +} diff --git a/icons/readme.md b/icons/readme.md new file mode 100644 index 0000000..612614a --- /dev/null +++ b/icons/readme.md @@ -0,0 +1 @@ +Here is where you put `.png` files to use as icons for a list. For a list with the name *Music Practice*, you should put a file called `music_practice.png` in this directory. diff --git a/index.js b/index.js new file mode 100644 index 0000000..ee2f588 --- /dev/null +++ b/index.js @@ -0,0 +1,8 @@ +const alfy = require('alfy') + +const userPermissionToken = alfy.config.get('userPermissionToken') +const developerKey = alfy.config.get('developerKey') +const boardId = alfy.config.get('boardId') + +// TODO: finish this module. Should prompt user to provide userPermissionToken +// etc. if not present. Otherwise, should call show-lists module. diff --git a/info.plist b/info.plist new file mode 100644 index 0000000..f066bde --- /dev/null +++ b/info.plist @@ -0,0 +1,307 @@ + + + + + bundleid + io.noisemachines.write-that-down + category + Internet + connections + + 218784CB-0933-4076-A2D6-5B9F64C4C1B5 + + + destinationuid + 6AA671B5-1C8C-40F5-910B-7D2739C02571 + modifiers + 0 + modifiersubtext + + vitoclose + + + + 50B0CD10-4D52-4898-9C05-DD3A34A829C3 + + + destinationuid + EC0443C7-32F6-4FAE-91C5-3A9B951F4844 + modifiers + 0 + modifiersubtext + + vitoclose + + + + 6AA671B5-1C8C-40F5-910B-7D2739C02571 + + + destinationuid + 8E5BF5DE-04C3-4A82-BEEA-2171544DCD70 + modifiers + 0 + modifiersubtext + + vitoclose + + + + 8E5BF5DE-04C3-4A82-BEEA-2171544DCD70 + + + destinationuid + DFEC23B9-8D16-4012-9434-3686421EBC43 + modifiers + 0 + modifiersubtext + + vitoclose + + + + DFEC23B9-8D16-4012-9434-3686421EBC43 + + EC0443C7-32F6-4FAE-91C5-3A9B951F4844 + + + destinationuid + 218784CB-0933-4076-A2D6-5B9F64C4C1B5 + modifiers + 0 + modifiersubtext + + vitoclose + + + + + createdby + C. Thomas Bailey + description + Interact with Trello inside Alfred + disabled + + name + Write That Down + objects + + + config + + alfredfiltersresults + + argumenttype + 1 + escaping + 127 + keyword + show actions + queuedelaycustom + 3 + queuedelayimmediatelyinitially + + queuedelaymode + 1 + queuemode + 2 + runningsubtext + Thinking... + script + ./node_modules/.bin/run-node show-actions.js "$1" + scriptargtype + 1 + scriptfile + test.sh + subtext + + title + Find or create a card + type + 0 + withspace + + + type + alfred.workflow.input.scriptfilter + uid + 6AA671B5-1C8C-40F5-910B-7D2739C02571 + version + 2 + + + config + + alfredfiltersresults + + argumenttype + 1 + escaping + 127 + keyword + trello + queuedelaycustom + 1 + queuedelayimmediatelyinitially + + queuedelaymode + 0 + queuemode + 1 + runningsubtext + Thinking... + script + ./node_modules/.bin/run-node show-lists.js "$1" + scriptargtype + 1 + scriptfile + test.sh + subtext + Work with Trello in Alfred + title + Write That Down + type + 0 + withspace + + + type + alfred.workflow.input.scriptfilter + uid + 50B0CD10-4D52-4898-9C05-DD3A34A829C3 + version + 2 + + + config + + concurrently + + escaping + 102 + script + ./node_modules/.bin/run-node handle-action.js "$1" + scriptargtype + 1 + scriptfile + + type + 0 + + type + alfred.workflow.action.script + uid + DFEC23B9-8D16-4012-9434-3686421EBC43 + version + 2 + + + config + + argument + Selected a list. + cleardebuggertext + + processoutputs + + + type + alfred.workflow.utility.debug + uid + EC0443C7-32F6-4FAE-91C5-3A9B951F4844 + version + 1 + + + config + + argument + + variables + + listId + {query} + + + type + alfred.workflow.utility.argument + uid + 218784CB-0933-4076-A2D6-5B9F64C4C1B5 + version + 1 + + + config + + argument + Selected an action. + cleardebuggertext + + processoutputs + + + type + alfred.workflow.utility.debug + uid + 8E5BF5DE-04C3-4A82-BEEA-2171544DCD70 + version + 1 + + + readme + Alfred's Script Filter object is extremely powerful, and gives you control over the results displayed in Alfred. + +Alfred 3 now supports JSON, which is the recommended format. XML is also supported for legacy Alfred 2 workflows, and has been updated to include the new features. + +A new option now exists for Script Filters, "Alfred filters results". This allows you to return a whole list of results, let Alfred do the subsequent filtering with extreme performance. When this option is selected, your script will be called once with no argument. + uidata + + 218784CB-0933-4076-A2D6-5B9F64C4C1B5 + + xpos + 300 + ypos + 200 + + 50B0CD10-4D52-4898-9C05-DD3A34A829C3 + + xpos + 60 + ypos + 170 + + 6AA671B5-1C8C-40F5-910B-7D2739C02571 + + xpos + 380 + ypos + 170 + + 8E5BF5DE-04C3-4A82-BEEA-2171544DCD70 + + xpos + 560 + ypos + 200 + + DFEC23B9-8D16-4012-9434-3686421EBC43 + + xpos + 650 + ypos + 170 + + EC0443C7-32F6-4FAE-91C5-3A9B951F4844 + + xpos + 230 + ypos + 200 + + + version + 0.1 + webaddress + https://github.com/noise-machines/write-that-down + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..4e88975 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "alfred-ideas", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "alfy": "^0.3.0", + "node-notifier": "^4.6.1", + "node-trello": "^1.1.2", + "open": "0.0.5" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..1b4ef7b --- /dev/null +++ b/readme.md @@ -0,0 +1,29 @@ +# Write That Down + +*Write That Down* is an Alfred 3 workflow. It lets you interact with Trello right in Alfred. Perfect for jotting down quick ideas. + +We're still in alpha, so documentation will be minimal. We'll add support for non-technical users in beta. + +## Features + +- For now, only supports interacting with a single board. +- Search all lists and cards in a board. +- Action a search result to open the card in your default web browser. +- When creating a new card, use `;` to separate the title from the card description. For example: + ``` + This is the title of my new card; this is the description. + ``` + +## Limitations + +**Connecting to Trello** + +While in alpha, you'll have to set up the Trello API keys and whatnot yourself. Take a look at `trello.js` to see what information *Write That Down* requires. + +**Setting up icons** + +*Write That Down* can use specific icons for different lists. If you have a list named `Music Practice`, *Write That Down* will look for an icon at `./icons/music_practice.png`. + +## License + +[MIT](https://opensource.org/licenses/MIT) © 2017 [C. Thomas Bailey](https://github.com/noise-machines) diff --git a/show-actions.js b/show-actions.js new file mode 100644 index 0000000..419e2a7 --- /dev/null +++ b/show-actions.js @@ -0,0 +1,29 @@ +const alfy = require('alfy') +const trello = require('./trello') +const iconFor = require('./icon-for') + +const formatCard = ({ id, name, url }) => ({ + title: name, + arg: JSON.stringify({action: 'show', url}), + quicklookurl: url +}) + +const listId = process.env.listId + +const parts = alfy.input.split(';') +var title = parts[0] +if (title.trim().length === 0) title = 'Create a new card' +const subtitle = parts[1] +const newCard = { action: 'create', card: { name: title, desc: subtitle, pos: 'top', idList: listId } } +const newCardItem = { + title, + subtitle, + arg: JSON.stringify(newCard), + icon: iconFor('new card') +} + +trello.getCards(listId).then(cards => { + const items = alfy.inputMatches(cards, 'name').map(formatCard) + items.unshift(newCardItem) + alfy.output(items) +}) diff --git a/show-cards.js b/show-cards.js new file mode 100644 index 0000000..d43e869 --- /dev/null +++ b/show-cards.js @@ -0,0 +1,14 @@ +const alfy = require('alfy') +const { getCards } = require('./trello') + +const formatCard = ({ id, name, url }) => ({ + title: name, + arg: url, + quicklookurl: url +}) + +const listId = process.env.listId +getCards(listId).then(cards => { + const items = cards.map(formatCard) + alfy.output(items) +}) diff --git a/show-lists.js b/show-lists.js new file mode 100644 index 0000000..997137f --- /dev/null +++ b/show-lists.js @@ -0,0 +1,14 @@ +const alfy = require('alfy') +const { getLists } = require('./trello') + +const formatList = ({ id, name, icon, uid }) => ({ + title: name, + arg: id, + icon, + uid +}) + +getLists().then(lists => { + var items = lists.map(formatList) + alfy.output(items) +}) diff --git a/trello.js b/trello.js new file mode 100644 index 0000000..dca767a --- /dev/null +++ b/trello.js @@ -0,0 +1,57 @@ +const alfy = require('alfy') +const { userPermissionToken, developerKey, boardId } = require('./secrets') +const Trello = require('node-trello') +const t = new Trello(developerKey, userPermissionToken) +const iconFor = require('./icon-for') + +function assignUid (resultType) { + return function (element) { + element.uid = `${resultType}/${element.id}` + return element + } +} + +function get (path, resultsType) { + const refreshCache = new Promise((resolve, reject) => { + t.get(path, (err, results) => { + if (err) { + reject(err) + } else { + results = results.map(assignUid(resultsType)) + alfy.cache.set(path, results) + resolve(results) + } + }) + }) + + const cached = alfy.cache.get(path) + if (cached) return Promise.resolve(cached) + return refreshCache +} + +function post (path, args) { + return new Promise((resolve, reject) => { + t.post(path, args, (err, result) => { + if (err) { + reject(err) + } else { + resolve(result) + } + }) + }) +} + +module.exports = { + getCards (listId) { + return get(`/1/lists/${listId}/cards`, 'card') + }, + getLists () { + return get(`/1/boards/${boardId}/lists`, 'list').then((lists) => { + lists.forEach(list => list.icon = iconFor(list.name)) + return lists + }) + }, + postCard (card) { + return post(`/1/cards`, card) + } +}