I’ve been using the Phanpy fedi client written by Chee Aun for a while now and am very fond of it. It’s a progressive web app written in JavaScript, so you can use on lots of platorms and it’s pretty much identical - but adapts dynamically for smaller and larger screens. I run my own self-hosted copy because I’m that kind of person. This is what it looks like in “TweetDeck” mode, where you see multiple columns rather than just your main timeline:
The Itch #
Now I’m a keyboard-centric computer user, and if possible I like to be able to use keyboard shortcuts to get around. Phanpy has some very handy key bindings for common things like shifting focus from column to column, boosting, liking and so on.
One particularly useful shortcut is ., which loads new posts in the column
which currently has focus. But annoyingly . doesn’t didn’t work for
notirfications, so you have had to poke around with your mouse, TrackPoint
etc and click on the Notifications label at the top of the column.
Scratch, scratch #
For Mayke I thought I might take a look and see what I could do about that. The
Phanpy sources are nice discrete files
grouped together by function, so it’s not difficult to zero in on the chunk of
code that’s run when you hit the . key.
I wombled around for a bit in the codebase and took a look at the project’s issues and pull requests in case someone else had beaten me to it. There were a couple of people noting that Notifications behaved differently to the other columns, and some comments from Phanpy team members about how this was a side-effect of slicing and dicing different types of data to present a unified view.
At first I was discouraged by this, but then I remembered that when you click
on the Notifications heading - it runs some code. Then I went back to the
codebase to look in src/pages/notifications.jsx to see what actually happens,
and it turns to be fairly simple:
loadNotifications(true);
scrollableRef.current?.scrollTo({
top: 0,
behavior: 'smooth',
});Well now - that wasn’t too hard, was it?
I don’t even really know JavaScript but I can kind of see what’s going on here.
There is a little more code required to handle the key binding, but that can be
gleaned from the existing . handler code in src/components/timeline.jsx:
const handleLoadNewPosts = useCallback(() => {
if (showNewPostsIndicator) loadItems(true);
scrollableRef.current?.scrollTo({
top: 0,
behavior: 'smooth',
});
}, [loadItems, showNewPostsIndicator]);
const dotRef = useHotkeys('.', handleLoadNewPosts, {
useKey: true,
ignoreEventWhen: (e) => {
// Allow '.' even with Shift (some keyboard layouts require Shift for '.')
if (e.key === '.') return false;
return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
},
});Munging those two code fragments together, we have this nice little self-contained patch which only changes a very small part of one file:
diff --git a/src/pages/notifications.jsx b/src/pages/notifications.jsx
index 54df1c45..6034d588 100644
--- a/src/pages/notifications.jsx
+++ b/src/pages/notifications.jsx
@@ -646,6 +646,25 @@ function Notifications({ columnMode }) {
},
);
+ const dotRef = useHotkeys(
+ '.',
+ () => {
+ loadNotifications(true);
+ scrollableRef.current?.scrollTo({
+ top: 0,
+ behavior: 'smooth',
+ });
+ },
+ {
+ useKey: true,
+ ignoreEventWhen: (e) => {
+ // Allow '.' even with Shift (some keyboard layouts require Shift for '.')
+ if (e.key === '.') return false;
+ return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
+ },
+ },
+ );
+
const today = new Date();
const todaySubHeading = useMemo(() => {
return niceDateTime(today, {
@@ -664,6 +683,7 @@ function Notifications({ columnMode }) {
jRef.current = node;
kRef.current = node;
oRef.current = node;
+ dotRef.current = node;
}}
tabIndex="-1"
>And that’s it - the itch is scratched, and we can do a nerd happy dance!
Grooming the digital commons #
But actually, that’s not it, because I don’t really want to have to patch
Phanpy every time there’s a new version. And I already know that there are
other people who were interested in making the . key binding work
consistently across the app.
So here’s what I did…
I forked the Phanpy source code repo and sent the maintainers a pull request with the diff, and lo and behold my modest tweak is now part of the Phanpy codebase. Actually my original patch was slightly different, and I modified it based on their feedback, but let’s keep the story simple…
Here’s the Phanpy source code commit which adds my change. Let’s hope it doesn’t break anything - at least it is quite self-contained!
No kings #
I’ve contributed various little bits and bobs to projects in the past, but not really reflected on how different things like this are to the conventional view of software development in the sense of big and complicated projects. This kind of software development is more like a community garden or allotment, where anyone can chip in and help out, and everyone benefits.
Of course things aren’t always harmonious, and there are some famously divisive figures in the free and open source software movement. But perhaps more importantly, many of us have been socialised to think that everything must have a Leader, Boss, Director, Organiser and so on. If this is what you’ve grown up with, and see all around you, the idea of a mutual aid approach like the community garden or randomly contributing little chunks of unsolicited code that you found useful can seem quite bizarre.
You might think perhaps we’ve been doing software development all wrong…, but I wonder whether the bigger message is that perhaps we’ve been doing society all wrong.