JavaScript Mapping Library
Here’s a quick write-up of something I actually built a week or so ago… and forgot to share on the blog. Instead, I shared a way to integrate your Mastodon with Eleventy. In that post, I mentioned how the account Fedi.Tips was looking for ways to let people view their archived Mastodon data. I followed their guide on getting my export and started work on a simple viewer.
If you don’t care about the "how", and have your archive ready, just head on over to https://tootviewer.netlify.app and upload your zip.
When you get your Mastodon archive, it will be a zip file containing the following:
I decided to focus mainly on actor.json and outbox.json, figuring toots would be the most critical thing folks would want to examine.
At a high level, the application consists of:
I’m not going to over every line of code (I’ll share a link to the repo at the end), but here are some interesting bits.
There re two ways to select your archive, either via a simple input[type=file] or drag and drop. I covered how to do this in-depth here: "Using Drag/Drop in Alpine.js with PDF Embed"
input[type=file]
Once you have a handle to the file, I then begin the process of parsing the zip in a function named loadZip. I begin with a bit of validation:
loadZip
this.status = 'Checking zip file.';let zip = new JSZip();let zipContents = await zip.loadAsync(file);let names = Object.keys(zipContents.files);if(!this.validateArchive(names)) { this.status = 'This does not appear to be a valid Mastodon archive.'; return;}
The validateArchive function does simple sanity checking on the entries of the zip:
validateArchive
validateArchive(names) { /* our 'rules' for valid archive are: must have outbox.json and actor.json this could be improved */ return names.includes('outbox.json') && names.includes('actor.json');}
Going back to loadZip, I can then read my two files (again, I’m only concerned with toots and your profile):
// read in actor and outbox for processingthis.actorData = JSON.parse((await zipContents.files['actor.json'].async('text'))); this.messageData = JSON.parse((await zipContents.files['outbox.json'].async('text'))).orderedItems.filter(m => m.type === 'Create');
Notice that I do a filter on your toots to focus on items related to writing toots. The data also seemed to include things like announcements of accounts I followed and such, so this filter removed any noise.
Finally, I store all of this in localStorage. This made testing a heck of a lot easier as I could reload and skip uploading the zip, but if I do decide to work with media, I’ll need to switch to IndexedDB instead.
The UI is still very much a work in progress, but I focused on showing your profile first:
You’ll notice I did not render your avatar and header. I absolutely could have, and heck, may add that in a few minutes as I literally just thought of a nice way of doing that and storing it in local storage.
Beneath the profile view is the most important bit, your toots:
You’ll notice on top there is a quick filter and pagination. I’ve got nearly two thousand toots so pagination was required and the search lets me find anything by keyword. As I mentioned, I’m not handling media yet so you won’t see attached pictures in the view here, but each individual toot is linked to the original for quick view in the native Mastodon view.
If you’ve got ideas or suggestions, please head over to the repo (https://github.com/cfjedimaster/tootviewer) and let me know. I’m open to any PRs as well. As I said above, I think I’ll quickly add your profile pics to the UI, but outside of that, I think the main thing I want to tackle is supporting media in the toot display and perhaps a general media browser. (You may remember posting a particular picture but have forgotten what you typed about the picture.) I hope this helps folks out!
Raymond Camden
You must be logged in to post a comment.
This site uses Akismet to reduce spam. Learn how your comment data is processed.