Photo Bucket Update: More On Galleries
Spent a bit more time working on Photo Bucket this last week1, particularly around galleries. They’re progressing quite well. I’m made some strides in getting two big parts of the UI working now: adding and removing images to galleries, and re-ordering gallery items via drag and drop.
I’ll talk about re-ordering first. This was when I had to bite the bullet and start coding up some JavaScript. Usually I’d turn to Stimulus for this but I wanted to give HTML web components a try. And so far, they’ve been working quite well.
The gallery page is generated server-side into the following HTML:
<main>
<pb-draggable-imageset href="/_admin/galleries/1/items" class="image-grid">
<pb-draggable-image position="0" item-id="7">
<a href="/_admin/photos/3">
<img src="/_admin/img/web/3">
</a>
</pb-draggable-image>
<pb-draggable-image position="1" item-id="4">
<a href="/_admin/photos/4">
<img src="/_admin/img/web/4">
</a>
</pb-draggable-image>
<pb-draggable-image position="2" item-id="8">
<a href="/_admin/photos/1">
<img src="/_admin/img/web/1">
</a>
</pb-draggable-image>
</pb-draggable-imageset>
</main>
Each <pb-draggable-image>
node is a direct child of an <pb-draggable-imageset>
. The idea is that the user can rearrange any of the <pb-draggable-image>
elements within a single <pb-draggable-imageset>
amongst themselves. Once the user has moved an image onto to another one, the image will signal its new position by firing a custom event. The containing <pb-draggable-imageset>
element is listening to this event and will respond by actually repositioning the child element and sending a JSON message to the backend to perform the move in the database.
A lot of this was based on the MDN documentation for drag and drop and it follows the examples quite closely. I did find a few interesting things though. My first attempt at this was to put it onto the <pb-draggable-image>
element, but I wasn’t able to get any drop events when I did. Moving the draggable
attribute onto the <a>
element seemed to work. I not quite sure why this is. Surely I can’t think of any reason as to why it wouldn’t work. It may had something else, such as how I was initialising the HTTP components.
Speaking of HTML components, there was a time where the custom component’s connectedCallback
method was being called before the child <a>
elements were present in the DOM. This was because I had the <script>
tag in the the HTML head and configured to be evaluated during parsing. Moving it to the end of the body and loading it as a module fixed that issue. Also I found that moving elements around using element.before and element.after would actually call connectedCallback
and disconnectedCallback
each time, meaning that any event listeners registered within connectedCallback
would need to be de-registered, otherwise events would be handled multiple times. This book-keeping was slightly annoying, but it worked.
Finally, there was moving the items with the database. I’m not sure how best to handle this, but I have that method that seems to work. What I’m doing is tracking the position of each “gallery item” using a position
field. This field would be 1 for the first item, 2 for the next, and so on for each item in the gallery. The result of fetching items would just order using this field, so as long as they’re distinct, they don’t need to be a sequence incrementing by 1, but I wanted to keep this as much as possible.
The actual move involves two update queries. The first one will update the positions of all the items that are to shift left or right by one to “fill the gap”. The way it does this is that when an item is moved from position X to position Y, the value of position
between X and Y would be changed by +1 if X > Y, or by –1 if Y > X. This is effectively the same as setting position X to X + 1, and so on, but done using one UPDATE
statement. The second query just sets the position of item X to Y.
So that’s moving gallery items. I’m not sure how confident I am with this approach, but I’ve been testing this, both manually and by writing unit tests. It’s not quite perfect yet: I’m still finding bugs (I found some while coming up with these screencasts). Hopefully, I’ll be able to get to the bottom of them soon.
The second bit of work was to actually add and remove images in the gallery themselves. This, for the moment, is done using a “gallery picker” which is available in the image details. Clicking “Gallery” while viewing an image will show the list of galleries in the system, with toggles on the left. The galleries an image already belongs to is enabled, and the user can choose the galleries they want the image to be in by switching the toggles on and off. These translate to inserts
and remove
statements behind the scenes.
The toggles are essentially just HTML and CSS, and a bulk of the code was taken from this example, with some tweaks. They look good, but I think I may need to make them slightly smaller for mouse and keyboard.
I do see some downside with this interaction. First, it reverses the traditional idea of adding images to a gallery: instead of doing that, your selecting galleries for an image. I’m not sure if this would be confusing for others (it is modelled on how Google Photos works). Plus, there’s no real way to add images in bulk. Might be that I’ll need to add a way to select images from the “Photos” section and have a dialog like this to add or remove them all from a gallery. I think this would go far in solving both of these issues.
So that’s where things are. Not sure what I’ll work on next, but it may actually be import and export, and the only reason for this is that I screwed up the base model and will need to make some breaking changes to the DB schema. And I want to have a version of export that’s compatible with the original schema that I can deploy to the one and only production instance of Photo Bucket so that I can port the images and captions over to the new schema. More on this in the future, I’m sure.
-
Apparently I’m more than happy to discuss work in progress, yet when it comes to talking about something I’ve finished, I freeze up. 🤷 ↩︎
Huh, so apparently it’s possible to scheduled Github actions to run like a cron-job. That’s really interesting to know.
Found this while reading this post about setting up an RSS feed to Google Chrome’s Dev blog.
Feels a little like bush week this morning.
Signed up as a lifetime member to Scribbles. Given how fun it is to use, it was an easy decision. Fantastic work, Vincent.

Finally got a new release of Dynamo-Browse out today. Realise the last one was 9 months ago, which is a bit long in the tooth (I’ve been using dev builds so the changes were beginning to pile up). Also updated the documentation which needed a bit of TLC.
I probably should write about what’s actually in this release. I tried writing an announcement post, but it just didn’t sit right with me, so I junked it (writing about, or really “announcing” things I’ve done does not come easily to me). Not sure anyone is actually using this anyway, which didn’t help with motivation. I might write more about what’s in this release a bit later. In the meantime, if there is anyone using this, please refer to the release notes and let me know if there are any questions.
Was just at the supermarket using an automated checkout machine with the sound turned off. Must say, it’s so much better than the otherwise chatty machines I usually have to use.
Ready for some bocce. Back at Creswick St. Reserve, the OG bocce site.

Giving Insomnia a try for testing a REST API. Took a while to find the downloadable artefact, but first impressions are pretty good. Have yet to explore the more advanced features like chaining requests, but good to see that this is supported.
Found this DynamoDB item calculator which would estimate the size of DynamoDB records. Useful for those times when you’re wondering if you can get away with using BatchGetItem without worrying about unprocessed keys and the 16 MB limit.
Ah, the vanguards of Autumn have returned.

Need to find an alternative to Postman for API testing. It’s quite capable, but something just repels me from using it. Might be its insistence that I should make an account, or maybe because the UI feels inefficient and clunky to use. I’d like to like it, but it’s just not doing it for me.
Random fact I found on Wikipedia today: apparently marsupials are less likely to contract rabies because they have a slightly lower body temperature than placental mammals (35°C vs. 37°C or so).
Only reason why I searched for this is that we’ve got a possum trap setup at work and someone asked if one could get rabies from a possum bite. I’m pretty sure rabies is not really a major problem here in Australia, but it’s interesting to know that marsupials are naturally more resistant to it for this reason (not that I’d be interested in testing this out myself).
If I were ever to make an RSS service like Feedbin, I’d probably add a feature which would delay some items until a specific day of the week. Some posts I get feel more suitable for weekend reading.
📺 Nintendo’s Luckiest Accident
So apparently the “watch” in Game & Watch actually references the time piece. Until I watched this video, I actually thought it meant watching the demo that played while in Time mode.
Just discovered that Stripe has a changelog, which is great. But they don’t offer it as an RSS feed, which is crazy. Seems like this would naturally translate into an RSS feed. They have one for their engineering blog so it’s not like they unfamiliar with the concept.
Getting a little tired of listening to podcasters talk about the Apple Vision Pro (I guess you have to be there). I’m more excited to hear about Manton talk about Micro.blog’s new notes feature on Core Intuition.
One Cup of Cappuccino Then I Go, by Paola Pivi
Saw this print while I was in Europe and liked it enough to buy a copy. Finally got it framed after several months, and now it’s on my wall. Turned out great.

🔗 The internet used to be ✨fun✨
Lot of interesting posts here about the personal web, both current and old school. I’ve been ducking in and out of this for a week now. Via the HV Discord.
Users of Go: don’t fear the zero value. Resist the urge to use string pointers for things that can be left unset. We need not live like Java developers (let’s not even mention null
and undefined
that our poor JavaScript brethren have to deal with). Learn to embrace the one nothing we have.
Complexity Stays At the Office
It’s interesting to hear what others like to look at during their spare time, like setting up Temporal clusters or looking at frontend frameworks built atop five other frameworks built on React. I guess the thinking is that since we use it for our jobs, it’s helpful to keep abreast of these technologies.
Not me. Not any more. Back in the day I may have though similar. I may even have had a passing fancy at stuff like this, revelling in its complexity with the misguided assumption that it’ll equal power (well, to be fair, it would equal leverage). But I’ve been burned by this complexity one to many times. Why just now, I’ve spent the last 30 minutes running into problem after problem trying to find a single root cause of something. It’s a single user interaction but because it involves 10 different systems, it means looking at 10 different places, each one having their own issues blocking me from forward progress.
So I am glad to say that those days are behind me. Sure, I’ll learn new tech like Temporal if I need to, but I don’t go out looking for these anymore. If I want to build something, it would be radically simple: Go, Sqlite or PostgreSQL, server-side rendered HTML with a hint of JavaScript. I may not achieve the leverage these technologies may offer, but by gosh I’m not going to put up with the complexity baggage that comes with it.