Replied to
IndieWeb Link Sharing
A pain point of the IndieWeb is that it's sometimes not as convenient to share content as it is on the common social media platforms.
Posting a new short “note” on my site currently requires me to commit a new markdown file to the repository on Github. That’s doable (for a developer), but not really convenient, especially when you’re on the go and just want to share a quick link. Twitter and other social media platforms literally make this as easy as clicking a single button, which makes it tempting to just post stuff straight to them. That’s why I wanted to improve this process for my site.
A quick Google search revealed that smarter people have already solved that problem. I came across this blog post by Tim Kadlec who describes adapting someone else’s link sharing technique for his (Hugo-powered) blog. That just left me the task of adapting it for my setup (Eleventy, Netlify) and customizing a few details.
The new link sharing basically has three main parts:
- a small Javascript bookmarklet to act as a “share button”
- a form that collects and sends the shared link data, and
- a serverless function to process it and create a new file.
Here’s how they work together:
The Bookmarklet
The button to kick things off is just a small bit of Javascript that takes the current page’s title, URL and optionally a piece of selected text you may want to quote along with the link.
It then sends these things as GET parameters to mxb.dev/share
by opening a new window to it.
function(){
// get link title
var title = document.getElementsByTagName('title')[0].innerHTML;
title = encodeURIComponent(title);
// get optional text selection
var selection = '';
if (window.getSelection) {
selection = window.getSelection().toString();
} else if (document.selection && document.selection.type != 'Control') {
selection = document.selection.createRange().text;
}
selection = encodeURIComponent(selection);
// generate share URL
var url = 'https://mxb.dev/share/?title='+title+'&body='+selection+'&url'+encodeURIComponent(document.location.href)
// open popup window to sharing form
window.open(url,'Sharer','resizable,scrollbars,status=0,toolbar=0,menubar=0,titlebar=0,width=680,height=700,location=0');
})()
That bookmarklet looks like this:
Share on MXB
…and can then be dragged to the bookmarks bar for quick access.
The Sharing Form
At mxb.dev/share, I’ve created a small preact app. It will take the GET params passed in via the URL and generate a live preview of the resulting note, so I know what the end product will look like.
There’s also a form that will be pre-populated with the values, which lets me include additional information and edit everything before posting.
The form also has fields for the Github username and security token, necessary for authentification. My password manager will fill those in automatically.
The Handler Script
When I hit the submit button, the form will send the data along to another endpoint. I’ve built a serverless function to handle the processing, so I could theoretically send data from other sources there too and keep the posting logic in one place. Netlify Functions seemed to be a nice fit for this.
Here’s the full script if you’re interested. It reads the posted data and generates a new markdown file from it, called something like 2019-08-11-amphora-ethan-marcotte.md
:
---
title: "Amphora - Ethan Marcotte"
date: "2019-08-11T16:57:13.104Z"
syndicate: false
tags: link
---
...we've reached a point where AMP may "solve" the web's
performance issues by supercharging the web’s accessibility problem.
(via [@beep](https://twitter.com/beep))
[ethanmarcotte.com/wrote/amphora](https://ethanmarcotte.com/wrote/amphora/)
It will then use the Github API to post that file as a base64-encoded string to a predetermined location in the site’s repository (in my case the folder where I keep all my notes).
Here’s the core function responsible for that:
const postFile = async data => {
const { title, token } = data
const fileName = getFileName(title)
const fileContent = getFileContent(data)
const url = API_FILE_TARGET + fileName
const payload = {
message: 'new shared link',
content: Buffer.from(fileContent).toString('base64'),
committer: {
name: 'Max Böck',
email: '[email protected]'
}
}
const options = {
method: 'PUT',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/vnd.github.v3+json',
Authorization: `token ${token}`
}
}
return await fetch(url, options)
}
That’s pretty much it! After the file is committed, Netlify will kick in and re-build the static site with the new content. If I have marked the “syndicate to Twitter” flag, another script will then cross-post the link there. (More on that in Static Indieweb pt1: Syndicating Content).
Mobile Share Sheet
A caveat of this technique is the use on mobile. Javascript bookmarklets are not as easily available in mobile browsers, which complicates the process again.
Thankfully Aaron Gustafson recently pointed out that it’s possible to define a “Share Target” for Progressive Web Apps. That means if your site is a PWA (it probably should be), you can add an entry like this to its manifest file:
// site.webmanifest
{
...,
"share_target": {
"action": "/share/",
"method": "GET",
"enctype": "application/x-www-form-urlencoded",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
}
}
That little bit of JSON registers your site as an application that can share things, just like Twitter, WhatsApp and the others. So after I “install” my PWA (read: create a shortcut link on my device home screen), it shows up as an option in the native Android “share” dialog:
Selecting the “MXB” option will grab the current page title and URL and send them as GET args to my sharing form, just like the bookmarklet would on desktop. There’s still a small bug in there where the URL will be sent as the text
parameter, but that can be corrected with a bit of Javascript in the form app.
I’m quite happy with how this turned out, as it feels really simple and straightforward. One step closer to IndieWeb bliss!
Looks like you've come up with a nice solution that works for you 👍
You should check out micropub it's kind of a open specification for what you've already built, then you can use any micropub client to post to your own website!
Replied to
Robin on Twitter
So... what's your favourite error message?
— Robin (@Melophilus) July 25, 2019
418 "I'm a teapot" of course! https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418
Replied to
Great post!
Webmentions are indeed not super straightforward to implement if you are building them yourself, but the same could be said about any comment system.
The difference is a lot of CMSs and tools have already abstracted the complexity of comments but not so much with webmentions. But I am sure that will change with increased adoption.
Replied to
Yeah it's super handy! Although I sometimes find my server doesn't always generate a preview.
I actually just moved the repo this week and need to update that old one. New repo is https://github.com/alltogethernow/web
Replied to
Thanks Johan! I put a bit of work into how to subscription functionality for this newer version so I'm glad you like it
Replied to
Whatsapp is not nearly as bad. It doesn't need any personal details apart from a phone number and everything is end to end encrypted. I'm sure Facebook will get some tracking and ads in there at some point but they haven't yet.
Replied to
Yup and my photos cannot do myself or the scenery justice. I also need to get around to posting the rest of my travel photos. I am a bit slow with those
Replied to
See https://grant.codes/2017/11/23/5a172628023c9d2ae6fbbe9a for reasons why
Replied to
Renting a vehicle is the best way to go if you can. And I would say most of Utah or the North Pacific Coast are probably some of the best places to visit
Replied to
Good writeup! I love having my instagram feed in my reader. I did it the same way.
On your caveats:
- I manage to automatically syndicate stuff to instagram , but it uses their unofficial api and could die at any time.
- I think you discovered that you can only have once instagram cookie per browser at a time, so whenever that expires / changes is when the session is expired and you need to update granary. I don't know how long it lasts, but if you never touch instagram in the browser it is quite a while.
I can also highly recommend doing something similar for twitter via granary if you don't already have that set up.
In the future I hope to integrate granary into together in someway to make it trivial to follow public instagram and twitter accounts :)