Logging completed tasks with Things and Alfred

I’ve tried a few different methods of logging my completed tasks (notebook, notes app, a different notes app), but the friction of opening the book/app, finding the note, and logging the task was too high and I always abandoned the effort after a week.

I’ve finally got something that stuck: Things URL schemes + Alfred Custom Web Search.

Here’s how it works:

  1. I use the Link Builder to create the basic URL I need to pass to Alfred: things:///add?title={query}&completed=true
  2. I create a Custom Web Search in Alfred to take my completed task query and open it in Things. Having it marked at Completed automatically adds it to the Logbook, bypassing the Inbox.

Things logbook

Of course, you need Alfred and Things installed for this to work. They are tools I use every day. I highly recommend them.

You can customize the link however you like, by adding tags, projects, etc. I keep it simple and just have a completed task.

Want to use this? Here is a URL to add this directly to Alfred. (It will only work if you have Alfred installed.)

Goodbye Crash, Howdy Automattic

Yesterday was my last day at Crash.

A little over three years ago Isaac emailed me one morning asking if I had a few minutes for a phone call. I took it, then a month later I started full time at Praxis. A year and a half later we split the company and I went with the Crash team.

I’m very proud of the things I worked on with both teams. Here are a few highlights:

  1. Set up a custom curriculum portal for the Praxis students
  2. Designed and wrote a completely new curriculum for Praxis students
  3. Set up a talent portal where hiring managers could view student profiles and decide who to interview
  4. Ran the marketing team at Praxis for a few months, where I learned a ton about marketing, instituted some targeted lead capture systems with ConvertFlow, and did a deep-dive on retargeting and conversion tracking. This led to capturing tens of thousands of leads while I was there, and even more after I left.
  5. Migrating a Hubspot CRM and email automation system to Salesforce and Mailchimp
  6. Rebuilt the Praxis website from the ground up with a custom WordPress theme
  7. Leading a focused week where the entire team worked together in groups to create a course, including writing content and shooting videos to accompany it
  8. Creating Discover, a personality-style quiz that tells you which role is the best place to start your career. It took a few months, and I figured out the traits, archetypes, and mappings from scratch. As of this writing, 12,500 people have taken it.
  9. Some of the people who went through a Praxis apprenticeship when I started (and who I helped coach!) are now hiring their own teams or starting their own companies. This is incredibly rewarding.
  10. I set a goal at the beginning of 2019 to learn Ember.js enough to commit meaningfully to the codebase. By the end of the year I was shipping fully functional features and fixing bugs. I had over 1000 commits to the codebase last year.
  11. All in all, I estimate that I directly helped at least 500 people over those three years change the course of their lives by helping them land an awesome job with high growth trajectory. This will have compound returns for them over the course of their careers.

In addition to the above, I learned a ton about:

  • Sales
  • Marketing
  • Engineering
  • Coaching
  • Conflict resolution
  • Pitching

Morgan von Gunten wrote a nice goodbye for me in last week’s Crash Report.

I’m thankful for the time I had with these teams and what we worked on together. It was exactly what I needed at the time.

So why am I leaving?

I’ve been on the edge of burnout for the past 6 months. My role changed many times in the last three years. I’ve adapted rapidly to the changing needs of the company, wore many hats, and figured out a lot of stuff. That was fun for a while, then it got pretty taxing. I decided at the beginning of February that it was time for a change. I want to settle down into one role.

It is hard. I’ve poured my heart into these teams for the past three years. It is not an easy decision to walk away, but I think it is the best decision for me, my health, and my family.

I used the Crash philosophy to go land a new role. I did my research, crafted a personalized pitch, and sent the pitch in along with the regular application. I got an immediate reply and threw myself into landing the role through the trial.

Automattic was the first place I applied. Yes, I applied to a few other places as backup, but Automattic was the place I really wanted to go.

I have a long history with WordPress. This very blog started on WordPress back in 2008, and I’ve developed at least 30 WordPress sites since and helped Praxians with ~200 more. CookLikeChuck.com runs on WordPress, the Praxis curriculum portal runs on WordPress, and Crash’s content is powered by a decoupled WordPress instance. I’m a steadfast remote worker, and Automattic is one of the largest fully distributed companies, with 1100 people across 75 countries. I resonate deeply with their Creed and Open Source mission.

Thankfully, it worked out. I’m joining the WordPress.com Special Projects Team, working to empower people who are doing cool things, one website at a time.

I start at Automattic on Monday.

Advent of Code, Day 12: Ember Simple Auth

Day 12!

I tried to add user authentication with EmberFire and Ember Simple Auth. I get through the provider workflow with Twitter, see a new user in Firebase, see the correct user data in the network panel in Chrome, and the ember_simple_auth-session cookie has “authenticated” in the content field. But session.isAuthenticated never seems to be true.

I must be doing something wrong, but I don’t have the time to figure it out today. Time to keep packing. I’m buying a house tomorrow and moving in this weekend!

Advent of Code, Day 11: Rendering Charts from Ember Data

Day 11!

After some frustrating trial and error and searching through docs with lots of words I don’t quite understand, Dave Wasmer kindly helped me figure out how to get Ember Objects (the results of Ember data queries) in a format I’m used to working with: Something that looks like a regular array of objects, or “POJO” as Dave said.

The solution ended up being calling the map method on the array-but-not-really-array and toJSON on each item in that. Then I get something back that looks like [{name: ..., startDate:...}, {...}]

model.plants.map(c => c.toJSON()); 

This allowed me to render the charts with Ember Data stored in Firebase.

Tomorrow: Digging back in to D3’s update pattern and getting the chart automatically updating when adding a new plant. Dave’s helpful suggestion was to look into the “data down, action up” pattern.

Thanks, Dave!

Advent of Code, Day 10: Setting up Firebase

Day 10!

I had a conversation on Twitter with Sam Selikoff and Ilya Radchenko, then later on Slack with Ilya and Dave Wasmer about using Firebase + EmberFire + Ember Data vs GraphQL + Hasura (Sam’s suggestion) vs GraphQL + Fauna (Ilya’s suggestion) for my data needs. I appreciate their inputs and arguments, but I ultimately went with Firebase + EmberFire for two reasons:

  1. I don’t want to set up and maintain an API server for this project. That knocks out Hasura.
  2. Fauna looks cool, but I don’t know either Ember Data or GraphQL that well, so I’ll have to learn one. Firebase + EmberFire + Ember Data is a little more battle tested and there is more documentation and search results for it vs Fauna, so I’m going to stick with that. I don’t want to be stuck in the dark on this project or constantly bug Ilya or Dave about stuff I don’t know. I do that enough at work.

So that means I set up Firebase and EmberFire today. It was surprisingly easy! The quickstart guide was solid. I was saving Ember Data to Firebase and retrieving it in my routes in no time.

I modified my Add a Garden form to save to Firebase, then transitioned to the Edit route where I passed along the ID of the garden I just created an retrieved the garden record’s data in the route. Then I read the Ember Data relationships docs and was able to both save and retrieve a garden’s related plants.

Then I got stuck: I tried to pass the plant data to the calendar component to render the chart and I can’t get it to work. The data isn’t coming through as I expect. I assume it has something to do with relationships in Ember data as promises, but haven’t quite figured the solution out yet. So that will be tomorrow’s project.

I feel like I’m making progress, though! It was super satisfying to see my collections and documents appear in Firebase.

Advent of Code, Day 9: More Ember Data

Day 9!

  • My problems with yesterday’s ember data store not being recognized magically disappeared when deleting node_modules and running yarn to install them again. Node, man. So good, but so frustrating sometimes.
  • I got plants and gardens showing up in the ember data store after form submissions, which I verified with the Ember Inspector plugin.
  • I added a form for creating gardens.

garden data plant data

I thought I’d be able to get it all working locally before setting up EmberFire and Firebase, but after seeing that IDs for Ember Data are usually assigned on the server and having a talk with with Dave Wasmer and Ilya Radchenko, it sounds like I might be better off setting up Firebase now instead of trying to get Mirage to work. That is a little more than I have time for today, so that will be tomorrow’s goal.

Advent of Code, Day 8: Learning Ember Data

Day 8! Another short session of work on a plane before I go home and pack up my apartment. Today I finished separating different functional parts of my components into individual components and started integrating Ember data.

I hit an issue I wasn’t able to research since I wrote it only a place without internet access, so the below might be wrong.

My models:

export default DS.Model.extend({   garden: DS.belongsTo('garden'),   startDate: DS.attr('date'),   name: DS.attr('string'),   color: DS.attr('string'),   daysToMaturity: DS.attr('number') });  export default DS.Model.extend({   name: DS.attr('string'),   startDate: DS.attr('date'),   plants: DS.hasMany('plant') }); 

Tomorrow’s goal:

  • Figure out why store is throwing errors for me

Advent of Code, Day 7: Figure out routes

Day 7! Today I’m traveling, but I got some work done on the plane: I figured out my routes and started breaking apart components to their individual functions.

My routes:

Router.map(function() {   this.route('login');   this.route('signup');   this.route('gardens', function() {     this.route('edit', { path: ':slug' });     this.route('new');   });   this.route('public-profile', { path: ':user_slug' }, function() {     this.route('garden', { path: ':garden_slug' });   }); }); 

Tomorrow’s goal:

  • Finish breaking the components apart
  • Get the new routes working

Advent of Code, Day 6: Plant Addition Flow

Day 6! Short update today after tons of phone calls, move packing prep, and packing for a trip.

Today’s progress

  • Removed the mocked plant data for my testing and built out the plant addition flow:
    • If there are no plants, hide the chart, show the form, and show a header
    • After adding a plant, hide the form, render the chart, and show the Add a Plant button
    • Only add the resize listener after the chart renders

I also tried to fix a bug with the color picker where it wouldn’t close, but didn’t solve it.

Amanda gave me another good idea today: For items like tomatoes, it would be good if there was another bar at the end that was lighter to signify a harvesting time window.

Today’s commits:

  1. first use flow

Tomorrow’s goal:

  • Dig in further to tailwind for styling
  • Read the Ember Data docs
  • Use D3’s update() to update the chart instead of destroying and redrawing it
  • Break the components apart

Advent of Code, Day 5: Form Components

Day 5! I didn’t think I’d make this much progress today, but I’m happy I did.

Today’s progress

  • Built reusable form components (button, input, form, label) with the help of EmberMap
  • Updated the chart
  • Integrated a color picker addon ember-pickr
  • Discovered and fixed a bug with the way I was calculating the scale.
  • I didn’t realize how much stuff I thought was just regular Ember in our app is actually addons. A bunch of things I use at Crash every day didn’t work at first in plant-gantt until I tracked it back to an addon. For example: ember-truth-helpers and ember-decorators

The scale bug: the scale bug You see here that blueberries are going off the right side. This is because I was calculating the scale based on the last plant in the list, which isn’t always the one that will be harvested last, only the one that will be planted last.

Instead, I had to use d3.max and offset to create a custom function to look at all of the plants and see which one would be harvested last:

let lastToHarvest = maxIndex(sortedData, d =>   timeDay.offset(new Date(d.start), d.daysToMaturity) ); 

Today’s commits:

  1. upgrade ember, add form components, add some helpers
  2. updates to chart with form data
  3. get the color picker working
  4. fixed end data calculation bug

Here’s how it works so far!

plant gantt with forms

Tomorrow’s goal:

  • Improve the form UI
  • Use D3’s update() to update the chart instead of destroying and redrawing it
  • Show a different screen on the first time you land instead of showing the mocked out chart
  • Break the components apart

Advent of Code, Day 4: Responsive D3 Chart in an Ember Component

Day 4!

Today’s progress

  • Sorted vegetables by start date
  • Updated the data model to calculate the harvest date by the days to maturity
  • Changed the scale to start 15 days before the first planting and end 15 days after the last harvest to save space on screen. This also allows things to start before January and end after December.
  • Made the chart responsive

Making the chart responsive took me down a weird rabbit hole. I tried using a computed property and then a bunch of different addons, but none of that worked. So I went the old-school route and wrapped the chart drawing in a function and then added an event listener to remove the chart and redraw it. There is probably a more elegant way to handle it, but I don’t know what it is.

window.addEventListener('resize', function() {   selectAll('svg').remove();   drawChart(); }); 

Today’s commits:

  1. change tick format to months and sort data
  2. move to relative scales
  3. responsive chart

Tomorrow’s goal:

  • Add a form to add new elements and update the chart

Advent of Code, Day 3: D3 chart basics

Day 3!

Today’s progress

  • set up pods because that is what I’m used to working with at Crash
  • configured Tailwind in Ember
  • successfully rendered yesterday’s D3 mock inside a component
  • Talked to my first user (my wife!) and got feedback about the chart. She says vegetables should be sorted by when you plant them by default, not when you added them to the chart.

Resources that helped me:

Today’s commits:

  1. configure tailwind, move to pods
  2. add calendar component and add D3 mock to it

Tomorrow’s goal:

  • change the calendar component to be responsive
  • sort the vegetables
  • Change the data to be closer to user-input data (plant date and duration, not just plant and harvest dates
  • watch more embermap videos to learn how to update with changes to user input data

Advent of Code, Day 2: D3 chart basics

Day 2! I woke up excited to work on this and spent an hour on it before work.

Today’s progress

Figured out the basics of the D3 implementation for Plant Gantt:

  • Learned how to use Observable
  • The week-based X axis
  • Stacking each plant similar to a horizontal bar chart and starting the bar at the correct start date
  • Lining the labels up correctly

Here is the Observable notebook.

Tomorrow’s goal

Get back into Ember land and figure out how to incorporate a chart with mocked out data in an Ember component.

Advent of Code, Day 1: Picking a Project

I’ve wanted to do Advent of Code for a few years, but I just can’t get excited about random puzzles. Not knocking all the folks involved in it–more power to you! It just isn’t for me. So I haven’t participated.

Last week while flipping through a seed catalog to figure out what I’m going to plan in my garden next year, I had an idea: I want to build a plant starting/harvesting calendar for my garden. Building that will be my own Advent of Code project this year.

Today was Day 1, planning day.

Name

Plant Gantt. Amanda came up with it and it is perfect. A gantt chart for plants.

Goals

  1. Build an interactive planting/harvesting calendar for vegetables.
  2. Deploy my own Ember app with a user login system on something like EmberFire
  3. Beef up on my D3 and Ember skills.
  4. Use Tailwind for the first time
  5. Make progress every day.

Features

  • Ability to add a plant name, seed start date, days to maturity, and pick a label color for it.
  • Visualize the plant’s growing period on a 52 week calendar
  • See the current day marked across the full calendar
  • Ability to hover over a plant and see how long it has been growing and how long left until harvest
  • Ability to create an account and save your calendar
  • Ability to print the calendar you created
  • Ability to create multiple calendars with unique names

I may not get this done in 25 days, but I intend to make progress every day. It will be tough with the month ahead: Two out of state trips, moving into our new house, and holiday parties. I’ll carve out time every day to get some work done, though.

Today’s progress

  1. Picked a name and got a domain
  2. Planned out the features and my goals for the project
  3. Set up the git repo and basic Ember app
  4. Install add-ons: ember-d3, ember-cli-tailwind

Tomorrow’s goal

  • Figure out the basics of the D3 gantt-style chart

How to download inline SVGs

I find myself regularly scouring business websites for logos to use. More and more of them use build pipelines that take all SVGs and embed them inline, making it impossible to just right click and download the image.

Here is my short workflow for downloading inline SVGs:

  1. Open up your inspector and copy the whole SVG element.
  2. Open your favorite text editor (mine is VSCode) and paste the whole SVG element you just copied into a new file and save it as companyname.svg
  3. Open your new companyname.svg in Sketch.app and change the colors if needed. Sometimes the colors are applied via CSS, which won’t come in with the SVG element.
  4. Export the logo as your filetype of choice. Mind the size because inline SVG elements are often small. I usually export PNGs at @2x.

Note: I still use steps 3 and 4 even if my end goal is SVG. First, I want to make sure the colors are correct. Second, Sketch adds some important attributes to SVGs like Viewbox that are helpful if you are using the file on the web.

Here is a video I made walking through the process:

Building a reading list with Jekyll data files

I transitioned by previous book notes section to a new reading list section. Why?

  1. I read a lot of fiction and don’t tend to take notes on it
  2. I wanted to keep a list of books I’ve finished.
  3. The book notes was not a great medium for doing #2. Writing the full set of notes was a pretty big barrier most of the time and I tended to go a few months at a time without updating the page, creating a large backlog.
  4. I came across Frank Chimero’s reading page and loved it. I also noticed that James Somers takes a similar approach with just listing the books out.

So, I decided to implement Frank Chimero’s design with my own reading list.

Instead of just hardcoding them in a page and repeating tons of HTML for the rest of my life, I decided to put the reading list in a Jekyll data file (I chose yaml) that I could easily add to and make a simple template to output.

First, I started with my previous book notes section. Since they were stored in a Jekyll Collection, I was able to write a quick Liquid for loop to generate most of my list for me. But I didn’t want to: include a full star rating, just a recommended star. So I chose to star books that I rated 5/5:

{% assign reviews = site.book_reviews | sort: "date" | reverse %} {% for review in reviews %}  - title: "{{ review.title }}"   author: {{ review.author }}   link: {{ review.book-link }}   star: {% if review.stars == 5 %}yes{% else %}no{% endif %}  {% endfor %}

The result looked like this:

- title: "Stories of Your Life and Others"   author: Ted Chiang   link: http://amzn.to/2Ghql7a   star: yes  - title: "A Burglar's Guide to the City"   author: Geoff Manaugh   link: http://amzn.to/2rElHgb  - title: "The Story of Sushi: An Unlikely Saga of Raw Fish and Rice"   author: Trevor Corson   link: http://amzn.to/2DAvzct  - title: "The Island at the Center of the World: The Epic Story of Dutch Manhattan and the Forgotten Colony That Shaped America"   author: Russell Shorto   link: http://amzn.to/2yqLmaE   star: yes 

Awesome! That spit me out a list of everything I’ve written book notes for. Since it is sorted by date in reverse chronological order, it was quick for me to manually break it up by year:

 - year: "2019"     books:       - title: The Summer Book         author: Tove Jansson         link: https://amzn.to/2ZMtUuC         star: yes        - title: "The Last Pirate of New York"         author: Rich Cohen         link: https://amzn.to/34h0Oa3         star: yes        - title: "Fall; or, Dodge in Hell"         author: Neal Stephenson         link: https://www.amazon.com/Fall-Dodge-Hell-Neal-Stephenson/dp/006245871X         star: yes  - year: "2018"     books:       - title: "The World is a Narrow Bridge"         author: Aaron Thier         link: https://amzn.to/2PK3OZc        - title: "Fashion Climbing"         author: Bill Cunningham         link: https://amzn.to/2PJqkgz        - title: "Cape Cod"         author: Henry David Thoreau         link: https://amzn.to/2ZIISBV        - title: "The Outermost House"         author: Henry Beston         link: https://amzn.to/2MOf3hb         star: yes 

Excellent. Two more steps:

  1. I want a “last updated” key so I only have to update one file. That means I need to nest the years under a parent key in order to access them.
  2. I need to manually add the books that weren’t in my notes but I know I read. This took me a few hours to get the books, authors, and links.

Once I added the “last updated” key, the yaml structure looked like this:

lastupdate: September 3, 2019 list:   - year: "2019"     books:       - title: The Summer Book         author: Tove Jansson         link: https://amzn.to/2ZMtUuC         star: yes        - title: "The Last Pirate of New York"         author: Rich Cohen         link: https://amzn.to/34h0Oa3         star: yes        - title: "Fall; or, Dodge in Hell"         author: Neal Stephenson         link: https://www.amazon.com/Fall-Dodge-Hell-Neal-Stephenson/dp/006245871X         star: yes        - title: The Boatbuilder         author: Daniel Gumbiner         link: https://amzn.to/2Lgvh0l    - year: "2018"       books:         - title: "The World is a Narrow Bridge"           author: Aaron Thier           link: https://amzn.to/2PK3OZc          - title: "Fashion Climbing"           author: Bill Cunningham           link: https://amzn.to/2PJqkgz          - title: "Cape Cod"           author: Henry David Thoreau           link: https://amzn.to/2ZIISBV          - title: "The Outermost House"           author: Henry Beston           link: https://amzn.to/2MOf3hb           star: yes 

Here’s how I loop through that data to create my reading page:

{% for entry in site.data.reading.list %}    

{{entry.year}}

{{entry.books | size}} books
{% endfor %}

Note that {{entry.books | size}} line: This is what outputs the number of books I’ve read in a particular year by spitting out the size of the books array. This is one of the big benefits of using a data file instead of hardcoded HTML or markdown. Just add a new book and everything else updates itself. Plus, I can use the data file for other things later if I so choose.

Here is the result:

 reading list

You can check it out over at the reading list page, or you can see the code over at my Jekyll Tools Github repository.

If you liked my book notes, don’t worry. They aren’t going anywhere. I’m keeping the pages up for posterity, but I’m not updating them any more.