Weekly Report: 14th October 2019
FrontierNav Windows
FrontierNav now supports windows. Not Windows, that's already supported. That is, you can now open up links in pop-out windows to keep them persistent between page navigations. This avoids needing to constantly click and around and go back and forth between pages.
I'm still working out the user experience on smaller screens, but it's not a priority given mobile apps tend to be geared towards a single window experience.
The worst part about this feature is getting the naming right. "Window" is such a generic term and it's used everywhere. I just settled with "AppWindow", though that doesn't help when naming variables. Calling everything appWindow
so that it doesn't clash with the window
global seems a bit too verbose.
Writing my own Router
I finally decided to replace the deprecated redux-little-router
dependency I had. I knew it was a relatively simple replacement so I kept putting it off. I even forked it to fix a few issues as it started to go through bit rot. When I did sit down to replace it, I realised a few things.
The idea behind a router for web apps does not need to be tied to the limitations of a URL. A router is really just state and the URL is a representation of that state. So I separated those two out.
Instead of having the URL be updated as part of the router's state, the URL update is just another observer to that state. This allowed me to easily decouple the web-specific details, that is using the History API to persist the route, from the state itself which the rest of the app can use.
Since I have a few years worth of existing code using the URL to decide what to show, this decoupling is still a work in progress. I still have a lot of hardcoded strings linking different pages and routes of the app.
Freedom from Dependencies
Writing my own router is what pushed me to finally implement multiple windows. I'm now able to have multiple routers in the state for each window which greatly simplifies the logic around creating and navigating windows.
Currently, only one router is persisted in the URL, but nothing's really stopping me from persisting all of them. User experience wise, there's still clearly a "main" window, while the others are more ephemeral, so persisting them in the URL doesn't make much sense. Persisting them in local or remote storage might be more useful.
Anyways, if there's one thing I learnt from this, it's to not let your dependencies, libaries and frameworks narrow your thinking. If it prevents you from doing something, it's time to let go of it. You'll save a lot more time than working around it, which will only couple you to it even more.
Typesafe Routing
I implemented an API for type-safe routing to go with my router. The main goal was to remove the hard coded strings used to link pages.
// Route
"/explore/:gameId/entities/:entityId"
// Path using a hardcoded string
"/explore/astral-chain/entities/enemy-123"
// Path using a Type-safe API and strings for IDs
root().explore('astral-chain').entity('enemy-123').build()
// Path using a Type-safe API and typed objects with IDs
root().explore(game).entity(entity).build()
This has a number of advantages:
- Both the routes and the API for creating paths can be created in a single declaration to avoid inconsistencies.
- It's impossible to have an invalid route or typo.
- Autocompletion will kick in so I can see which routes are available.
- I can change the output of
build
without going through every link. - I can find where each route is used using code discovery rather than loose text searches.
I haven't rolled this out yet as I'm still not 100% on how I want to structure the new router state.
Once all of this is complete, I'll have a new open source library to release.
FrontierNav Data Tables
The spreadsheet-like data tables are gradually becoming more and more feature rich. The tables now support sorting by column, importing pre-formatted CSVs, moving entities and various other quality of life improvements. No doubt it will keep getting better as I import more game data.
Hiding Behind Cloudflare
Most of my public infrastructure is now behind Cloudflare. This should reduce the amount of maintenance I need to do in regards to legacy domains (like jahed.io) and security.
I've disabled HTTP access to my servers entirely and HTTP connections as well as legacy domains redirect using Cloudflare's Page Rules. No server needed.
While this does tie me more to Cloudflare, I am already reliant on it for reducing bandwidth costs so from an end user perspective, nothing's really changed. Moving traffic back to my server is a matter of removing SSL Client verification and adding the redirects.
Other Stuff
- I automated node-terraform releases last week, and this week I confirmed that it all works. It picked up two versions of Terraform, ran tests and published them. As usual when it comes to using CI services, I had to make some minor tweaks to fix YAML and environment runtime errors.
- I found out npm has a
deprecate
command so I went through my unmaintained packages and deprecated them.
Thanks for reading.