21°F 7:18am

Aaron Parecki

  • Articles
  • Notes
  • Projects
  • Day 15: Tags, Slug and Post Status for Quill #100DaysOfIndieWeb

    Wed, Jan 4, 2017 3:52pm -08:00

    I've been using Quill to write all these #100DaysOfIndieWeb posts, which is a great way to find the pain points in the interface. After having written 14 articles in the last 14 days, the main thing I want to be able to do is start an article here in the Quill editor, save it as a draft to my website, then open up the raw HTML to make fine-grained edits, and only after I'm done, actually publish it.

    I also added tags and slug fields so that I don't have to add those after the fact too!

    The "status" property is an experimental extension to Micropub, being documented here: https://indieweb.org/Micropub-extensions#Post_Status I started out this morning by documenting how Wordpress handles post status in their interface. That research has been captured on the wiki.

    In order to support this on my website, I first had to recognize the property in the Micropub request to set an internal flag for whether a post is a draft. When a post is a draft, it is not shown in lists to logged-out users, although if you know the permalink, you can see the post when you're logged out.

    I also never send Webmentions or publish to the WebSub hub for draft posts. I only send Webmentions after the post is published and publicly viewable.

    I'm also interested in incorporating the idea of "visibility" of posts into Micropub and my website. Flickr has a great implementation of this known as the "Guest Pass". I plan to research this more in the future to see if I can incorporate some of that into my website. 

    Portland, Oregon
    1 like 1 repost 1 mention
    #100daysofindieweb #100daysofcode #micropub #indieweb #quill
    Wed, Jan 4, 2017 3:52pm -08:00
  • Day 14: Posting to my Website from Alexa #100DaysOfIndieWeb

    Tue, Jan 3, 2017 5:23pm -08:00

    If you know me, you probably know that I log everything I eat and drink and post it to my website. A couple years ago, I wrote a small Pebble app that allowed me to quickly post common food and drink from my watch! Coincidentally around the time Pebble announced that FitBit had acquired their assets, my Pebble stopped working completely. This meant I no longer had a quick way to log food, and have to pull out my phone again to make log entries.

    This afternoon, Tantek suggested that I use my Amazon Alexa to post food and drink to my website instead! Of course this will only work when I'm at home, but it turns out that I'm home a lot of the time I'm eating and drinking. I also eat tacos every day, so it'd be great not to have to get out my phone during breakfast.

    So today, I launched Alexa integration for Teacup, the app I use to track my food.

    This was quite a challenging project given all the moving parts involved. I started by defining the voice interface I wanted to use. 

    Interaction Model

    Voice interactions for Alexa apps have to follow a pretty strict structure. Alexa doesn't support interpreting fully unstructured text, so app developers have to define patterns that Alexa can match on. Invoking any Alexa app involves first speaking the trigger word, followed by a keyword such as "ask" or "tell" followed by the app name, and then the pattern of text the app wants to match. So for Teacup, this results in speaking sentences such as:

    • "Alexa, tell Teacup I drank coffee"
    • "Alexa, tell Teacup I ate tacos"

    This gets turned into what Amazon calls the "Interaction Model", and is a list of "slots" along with corresponding keywords for each slot, as well as writing out some sample sentences.

    • LIST_OF_ACTIONS = ate | drank
    • LIST_OF_FOOD = Coffee | Cocktail | Beer | Tacos | Mac and Cheese | ...

    Sample utterances:

    • "I {Action} {Food}"
    • "I {Action} a {Food}"

    It isn't clear to me whether the list of keywords I provided is the complete set, because while I was testing, it managed to post the word "on" for the food, which is not in my list.

    Authentication

    The next challenge was linking user accounts between Amazon users and Teacup users. Amazon provides great documentation on this, and thankfully it's all based on OAuth 2.0 rather than having made up some other model themselves. Essentially, the Amazon Alexa app on your phone acts as an OAuth 2.0 client, and you have to build an OAuth 2.0 server into your app that it works against. This is a pretty clever solution actually. Luckily, I'm pretty familiar with OAuth 2.0, so I was able to build this out pretty quickly.

    One thing struck me about Amazon's recommendations about building OAuth support in your app. They say that they'll launch your authorization URL from inside the iOS app, which is a known antipattern for apps in general. In Amazon's docs, it says "The user logs in using their normal credentials for your site." This is a really bad idea. You never want to train your users to enter their passwords into random apps. This is the whole reason we have OAuth in the first place!

    Rather than asking people to enter passwords in the Alexa app, I opted to solve this a different way while still being compatible with Amazon's service.

    When you connect Teacup with the Alexa app, instead of asking for your Teacup password, it asks you to sign in from a real browser and generate a temporary device code.

    So you launch a real browser where you can be sure you're logging in to Teacup and not a phishing website, and then visit your settings screen which shows you this.

    Clicking the button generates a temporary code.

    Then you enter that code in the Alexa app, and it's able to connect the app session to your user account! This way you've never risked typing passwords into the Alexa app. Teacup takes the code entered, verifies that it's active, and then completes the OAuth 2.0 handshake with Amazon.

    Now you're logged in to Teacup in the Alexa app! This means the Alexa service now has an access token that it can use to talk to Teacup, which is how the two accounts are associated.

    It Works!

    Once I got everything hooked up, I had to put it to the test! Of course I can't risk posting fake data, so I had to open a beer and try it out.

    Here's the beer it posted to my site!

    If you want to see the code involved in making this work, head over to the Teacup GitHub repo. 

    In the mean time, I'm looking forward to telling Alexa I ate tacos tomorrow morning!

    Portland, Oregon
    5 likes 2 reposts 1 bookmark 1 reply 2 mentions
    #100daysofindieweb #indieweb #teacup #alexa #100daysofcode
    Tue, Jan 3, 2017 5:23pm -08:00
  • Day 13: Curved Lines for Atlas Static Maps #100DaysOfIndieWeb

    Mon, Jan 2, 2017 2:35pm -08:00

    Yesterday, Amy was asking if there was a library for drawing curved lines between two points on a map. She wants to use this for her travel posts, which say for example "Tokyo to Boston". We noticed that Facebook draws curved lines when they show a planned trip. Martijn was able to figure out how to reproduce this on Facebook consistently. You have to start by creating a "checkin" post, then add a "traveling to" activity to it. Notice that the line from Portland to Seattle is curved rather than straight, although it doesn't strictly follow a Great Circle line either.

    I liked this idea, and realized that my own travel posts would benefit from images as well, so I set to work adding this to the library that I use to generate the maps for my posts. Before, my posts just look like this. Wouldn't this look better with a map?

    My static maps are generated using Atlas, a service I wrote for handling all the geo stuff on my website. The static map generation is actually a surprisingly simple chunk of code, which assembles all the necessary map tiles and then draws points and lines on top of them using PHP GD and ImageMagick. 

    The first step was to figure out how to draw curved lines. There was more to it than I originally anticipated. I knew roughly the shape of the curve I wanted, and after researching a bit, determined that a Quadratic Bézier Curve would give me the shape that I want.

    A Quadratic Bézier Curve differs from a standard one in that only one control point (P1 in the image above) is used instead of two. Luckily, the PHP ImageMagick library has a function to draw that curve. If I use a control point at the midpoint of the start and end of each leg, I'll get a nice symmetric curve. I can vary the height of the curve by using a control point farther away from the line connecting the two points. Here's approximately what I'm going for, where A and B are the start and end points of the line segment, and P is the control point.

    Given A and B, I can find the mid-point M easily. Half the distance is d, so I know one side of the triangle, and I choose the angle alpha. It's been a long time since I've had to use any trig, so my memory of all this is rusty, but I did remember that it should be possible to find point P given one angle and one side of a right triangle. It took a while to dig up all the identities and formulas for this. Ultimately this stackexchange answer explained things well enough for me to reverse engineer a generic formula out of it.

    This means I can now vary the angle alpha to adjust how steep to make the curve. This translates into the following ImageMagick PHP code.

    $draw->pathStart();
    $draw->pathMoveToAbsolute($A['x'],$A['y']);
    $draw->pathCurveToQuadraticBezierAbsolute(
    $P['x'], $P['y'],
    $B['x'], $B['y']
    );
    $draw->pathFinish();

    We start at point A, then move through point P to land at B. With an angle of 25 degrees, this gives a pretty great looking curve!

    Once I started running this code for other trips from various points on the globe, I immediately ran into some interesting issues. It turns out these curves look best when the horizontal curves go upwards and the vertical curves go to the right, and when there are multiple hops, the lines must not overlap. Here are some images from my library with curves I'm relatively happy with.

    It took quite a bit of trial and error to determine the rules for making these render properly. The main trick is this: If the line is wider than it is tall, then draw the line from left to right. If the line is taller than it is wide, then draw from top to bottom. ("Draw from left to right" means choosing the leftmost point as point A in the equation above.)

    This rule ends up with pretty good results in most cases, as you can see above. However there are still a couple of cases I'm not 100% happy with, although I can't quite come up with a rule to fix them so I'm going to leave them as is for now.

    I think the curve in the Iceland to Germany hop should be flipped
    Here, the Detroit to Nashville hop should be flipped, I think because the obtuse angle looks wrong.

    I'm reasonably happy with the results regardless that I'm going to leave it alone for now. If you have any thoughts on fixing it, you're welcome to submit a pull request to Atlas!

    If you want to use this yourself, you're welcome to run the Atlas API on your own server, or just copy the code and use it as a library! To generate these curves, pass a parameter bezier=25 into the request when you are generating a line between two points.

    Now my itinerary posts look a lot better!

    Portland, Oregon
    3 likes 2 reposts 2 replies
    #indieweb #atlas #maps #100daysofindieweb #100daysofcode
    Mon, Jan 2, 2017 2:35pm -08:00
  • Day 12: Whitelist and Blacklist for OwnYourGram import #100DaysOfIndieWeb

    Sun, Jan 1, 2017 9:28am -08:00

    Today I added some additional settings to OwnYourGram. You can now configure a whitelist and/or blacklist of keywords to control which of your Instagram posts are imported to your website.

    I've been posting my #100DaysOfMusic posts on Instagram and having OwnYourGram post them back to my website, but over the past week I've found some problems with that approach. Instagram videos are limited to 60 seconds, and one of my songs was 90 seconds long. Instagram also runs some pretty heavy compression on the audio, which seems to be optimized for speech, so the music doesn't come through as cleanly as I wanted. 

    Because of this, what I've been doing is replacing the video on my website after OwnYourGram does the initial import. This workflow ends up being way too complicated, so what I'm going to start doing instead is posting the videos to my website directly. However, I still want to share them on Instagram, but I would end up with OwnYourGram posting another copy of it back to my website.

    With the new blacklist setting, now I can blacklist the "#100DaysOfMusic" tag so that OwnYourGram will skip importing those videos. While I was at it, I also added a whitelist in case someone has a use case for that as well. You can use the whitelist and blacklist together to make a rule that imports photos matching a keyword as long as another keyword is not present.

    I'd love to hear if you end yup using either of these settings!

    Portland, Oregon
    1 mention
    #ownyourgram #indieweb #100daysofindieweb
    Sun, Jan 1, 2017 9:28am -08:00
  • Day 11: Simpler fonts #100DaysOfIndieWeb

    Sat, Dec 31, 2016 1:58pm -08:00

    The other day I read a pretty funny blog post called "10 things I learned making the fastest site in the world". It's a little tongue in cheek, but overall has some good tips. One that caught my attention was the section on using web fonts.

    #10 Computers have nice fonts

    I’m always torn when it comes to web fonts. They’re a pain, performance-wise. But it’s nice to have nice things.

    I gave it a bit of a think-over for this site and came to a stunning realisation in four parts:

    • macOS has nice fonts
    • Android has nice fonts
    • Windows has nice fonts
    • iOS has nice fonts

    ...

    If you tell yourself you need the same, custom font on all devices then things have already gone too far and there is no hope for you.

    This is followed by a snippet of CSS called "good-enough.css".

    body {
        color: #212121;
        font-family: "Helvetica Neue", "Calibri Light", Roboto, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        letter-spacing: 0.02em;
    }

    This CSS includes fonts that are available on all the platforms listed, as well as tweaks the font smoothing used in WebKit and OSX.

    This has quickly become my favorite default font to use in projects rather than just "sans-serif" or loading a Google Web Font.

    I launched this change on my website, so you're likely reading this post in my new font style. Below I've included some screenshots showing the differences between the old and new fonts.

    Old Font

    This is the default font that ships with the Semantic-UI CSS framework which I use on my site.

    The font is called Lato, and it's one of the fonts available via Google Web Fonts, which means you have probably seen "loading fonts.googleapis.com" in the status bar when on my website at some point in the past. It's a nice font, but do I really need this text to look exactly like this? Probably not.

    Switching to System Fonts

    Here's what it looks like when I apply just the font-family CSS rule so that Helvetica Neue is rendered on OSX.

    I think it's perfectly acceptable.

    The Finished Product

    Adding in the tweaks to font-smoothing, you'll notice a subtle difference in the rendering. Suddenly the text looks a lot crisper!

    I'm happy with the look of this font, and also happy that I no longer need to rely on the Google Web Fonts service and make people load that extra HTTP request when viewing my site!

    Portland, Oregon
    1 mention
    #indieweb #p3k #100daysofindieweb #design
    Sat, Dec 31, 2016 1:58pm -08:00
  • Week in Review #100DaysOfIndieWeb

    Fri, Dec 30, 2016 2:18pm -08:00

    Webmention.io

    • Day 3: The avatars for comments/likes/etc that Webmention.io processes will now always be 256 pixels maximum. This avoids showing potentially large images shrunk down when displaying comments.
    • Day 10: HTML status pages for webmention.io 

    Quill

    • Day 4: The reply context on the note posting interface will truncate if the post you're replying to is large. 
    • The content area automatically expands when you type long notes.

    OwnYourGram

    • Day 5: There is now an interface in OwnYourGram you can use to post your 20 most recent photos again if you need to. This can be helpful when you're initially developing your Micropub endpoint, to test with real data from your Instagram account, and can be used to fill in the gaps if OwnYourGram misses some of your photos for some reason.
    • There is also a "disconnect" button you can use to stop OwnYourGram from processing your Instagram account.
    • Day 6: OwnYourGram now puts users into different tiers of polling frequency based on how often you post photos. This improves the responsiveness for heavy users, and reduces system load for users who don't post often.

    aaronparecki.com

    • Day 7: Changed the display of sparklines on my tag pages to be bar charts instead of a line.
    • Day 8: Now I can pin posts on tag pages!
    • Day 9: Added a Webmention form to the bottom of my posts.
    Portland, Oregon
    1 like 2 reposts 1 reply 1 mention
    #100daysofindieweb #indieweb #webmention #quill #p3k
    Fri, Dec 30, 2016 2:18pm -08:00
  • Day 10: HTML status pages for webmention.io #100DaysOfIndieWeb

    Fri, Dec 30, 2016 1:21pm -08:00

    Yesterday I added a Webmention form at the bottom of my posts. If you used this form, it would show you a "check status" link after accepting the Webmention request. My Webmentions are all handled by webmention.io, and its status URLs return a JSON response. This isn't particularly friendly when someone views one of these URLs in a browser, since they just see a raw JSON blob.

    Today I updated webmention.io to return all responses in HTML if they're made from a browser. It checks to see if there is text/html in the Accept header, and returns HTML if so, otherwise returns JSON as normal. Now when you view one of these status links, you'll see something like this.

    Since the Webmention spec doesn't define the body of the response, doing this is still considered conformant to the spec. The one non-standard thing I had to do was to return an HTTP 303 response when accepting the Webmention instead of 201, in order to get the browser to redirect to the status URL immediately. I still return 201 to non-browser clients so they won't see any change.

    Portland, Oregon
    3 mentions
    #100daysofindieweb #indieweb #webmention
    Fri, Dec 30, 2016 1:21pm -08:00
  • Day 9: Webmention form #100DaysOfIndieWeb

    Thu, Dec 29, 2016 9:27am -08:00

    I finally brought back the Webmention form on my website! At the bottom of my posts, you'll see a Webmention form now!

    If you've written a post on your own site that is a reply to my post, but don't yet send Webmentions automatically, then you can use this form to send a Webmention for you!

    The form submits directly to my Webmention endpoint at webmention.io via Javascript, so the results appear inline. (If you have Javascript disabled, then it works like a normal form post.) Since the actual Webmention processing happens asynchronously, you'll see a little confirmation message when you submit the form.

    Hopefully soon I'll have realtime comments appearing, so that your comment will appear inline automatically after it's done processing!

    Portland, Oregon
    1 like 4 mentions
    #100daysofindieweb #indieweb #webmention
    Thu, Dec 29, 2016 9:27am -08:00
  • Day 8: Pinned Posts #100DaysOfIndieWeb

    Wed, Dec 28, 2016 9:06am -08:00

    I've never really been a fan of using pinned posts on my Twitter profile, so I didn't expect this would be something I wanted on my own website. On my own home page I can just write whatever I want above my posts, so I don't really need "pinned posts" there either. However I just found a use for pinned posts, but not on my home page!

    I've been sharing the link to my 100 days tags with people as a way to share the content quickly. I wanted to be able to have a little intro post above the list of posts, and I realized that "pinned posts" matched the pattern of what I wanted pretty nicely! 

    So now, on tag and channel pages on my site, I can mark a post as "pinned" and it will always show up on top! The first post I've pinned is on my #100DaysOfIndieWeb tag page.

    My pinned posts appear with a green corner with a pushpin icon, above the list of other posts. The sparkline for the tag appears below the pinned post. The other thing pinning a post does is removes it from appearing in the list of posts for that tag where it would normally appear. However the pinned post will still appear in the normal positions for any other tags as well as my home page.

    To support pinned posts in p3k, I added custom properties to my Microformats2-based storage, called "p3k-pinned-category" and "p3k-pinned-channel". Any categories or channels listed in those properties will cause the post to be pinned there. Here's what the post looks like in my storage file.

    When my DB index processes this post to sort this post into category and channel lists, it catches the list of pinned categories/channels and adds a flag to the database table to mark that as pinned for the category.

    Another minor change I launched as part of this was fixing the rounded corner for my posts with colored corner flags. Previously, they looked like this:

    Now, the colored corner matches the rounded corners of the rest of the post:

    It's a subtle change but was bothering me for a while, and now that pinned posts means these corners will appear in more places, I wanted to fix it.

    Portland, Oregon
    3 mentions
    #pinned #100daysofindieweb #p3k #indieweb
    Wed, Dec 28, 2016 9:06am -08:00
  • Day 7: Nicer sparklines for tag pages

    Tue, Dec 27, 2016 2:37pm -08:00

    A while ago, inspired by @adactio, I had added sparklines to my home page and tag pages. After having them for a while, I realized that my tag pages would look better as sparkline bar charts instead of as a plain line.

    The lines looked fine for tags that had a lot of data, but didn't look good when there were only a few data points.

    I made a few changes that affect the display of these.

    The main difference was switching to rendering the data as a bar chart. I also added zero-padding to the data when the amount of data in the chart is less than the number of segments I want to display. Without that, the bar segments would be potentially different widths for different tags. This also does a better job of representing the use of a new tag on my site like #hamradio.

    I also realized that the SVG wasn't scaling on narrow screens, it was just cropping from the right. To fix this, I added a couple attributes to the main SVG tag, width="100%" and preserveAspectRatio="none". This allows the SVG to shrink its width while still maintaining the desired height!

    Portland, Oregon
    5 mentions
    #indieweb #100daysofindieweb
    Tue, Dec 27, 2016 2:37pm -08:00
load more

Hi, I'm Aaron Parecki, co-founder of IndieWebCamp. I maintain oauth.net, write about OAuth, and am the editor of the W3C Webmention and Micropub specifications, and co-editor of WebSub.

I've been tracking my location since 2008, and write down everything I eat and drink. I've spoken at conferences around the world about owning your data, OAuth, quantified self, and explained why R is a vowel.

  • IndieWebCamp Founder
  • W3C Editor
  • W7APK
  • These are a few of my favorite things.
  • All
  • Articles
  • Bookmarks
  • Notes
  • Photos
  • Reviews
  • Sleep
  • Travel
  • Contact
© 1999-2017 by Aaron Parecki. Powered by p3k. This site supports Webmention.
Except where otherwise noted, text content on this site is licensed under a Creative Commons Attribution 3.0 License.