Hey! Remember this? Probably not, but that’s OK because not only have I remembered for you, but like clockwork, I’m revisiting this project (literally: my archives say the last access of the original project was March of 2021).
For the uninitiated, here’s a recap of the significantly longer post: Adventure Outliner is a tool for TTRPG creators to write up their campaigns and modules. It allows creators to catalog their NPCs, encounters, items, and maps, but is primarily focused on the body of text that defines the content.
These were mockups of the intended look and feel, and when I last looked at this project, I had gotten a framework started. I had a working registration and login via Google’s Firebase and had started with some light NoSQL database work with Firestore. But me being me, I took a look at the old code through newer, more experienced eyes, and decided to throw it all out the window. That’s Agile development, right?
New and Actually Improved
This doesn’t look too much different from the original mockup, nor does it deviate much from the working model I had the last time I worked on this project. The devil is literally in the details, though.
I don’t say it here as often as I say “I suck at math”, but “I suck at design”. I really hate coming up with the ideas of design, and to be frank, despite almost 20 years of web development in various capacities, CSS is by far my weakest subject. The original site used centralized CSS the way your grandparents used it, and that’s still OK in 2022. However, newer methods exist to apply styles, and when we consider that React is all about components, the idea behind styled components makes a lot of sense.
A styled component defines styles that are specific to the display rendered by a single component.
A style is defined and used as if it were a component itself. Here, I’ve defined “HeaderRow” which is based on a div element. The CSS is defined using normal CSS syntax (it’s a bit closer to SCSS, really), and the styled component is used in place of the root element type. “HeaderRow” is the horizontal content that contains the site logo, the nav bar (unfinished), the search box (also unfinished), and the user presence badge. The CSS controls the layout and colors of the components placed inside of the “HeaderRow” div element. Why use this over central CSS or SCSS files?
The first reason is encapsulation. Since I started learning React, I have been trying to move towards embracing the “one concern, one component” mantra which states that for every element within a website, there should be one React component that controls and renders that concern, and that concern only. A search-box-and-button is a component. A button itself is a component. A nav bar is a component made up of interactive components like link or fancy-ass button components. Encapsulation keeps everything the component needs (within reason) inside the component definition. Whenever that component needs to be used, it’s just a matter of importing it and using it as-is (within reason, as no component is truly an island).
The second reason is specificity. I have needs for a button component, of course, but where that button is used might dictate a slightly different representation.
There are four buttons in this panel, but each of them looks different (and one has two states: enabled and disabled). Styled components allow me to define a base style, and then to derive from that base style to apply more specific styles based on what I need. In the end, I’ll define a single “core button” component, and will use styled component overrides to adapt other button-based components to the look and behavior that I need, where I need it, inside the component definitions for each button.
Firebase Version 9
Since I last worked this project, Google has updated their Firebase suite to version 9. The biggest takeaway that Google wants to give you is that V9 is now “tree-shakable”, which…is one of those eye-rolling tech terms that gets way over-used but is sadly the best way to explain what it does.
In version 8, Firebase was one monolithic import. If you only want to use authentication, or Firestore, or analytics, you could, but you had to carry the ponderous bulk of the Firebase library along for the ride. This made for large deployments as Webpack or Rollup or other bundler didn’t have any way to get rid of Firebase libraries that weren’t in use.
“Tree-shaking”, then, implies that when you shake a tree, all of the “loose” fruit will fall out. Version 9 now allows developers to include just those Firebase services that they need. When the project is bundled up, it will only include services in use, having “shaken out” those in the SDK which have no reference in the project.
I have found this to be a massive boon for revisiting this project because Firebase is now a bit more straightforward in how it implements itself.
One thing I had struggled with in the original project was how to authenticate users, and to keep them authenticated throughout the site. Last night I sat down and banged out a working implementation of Firebase Auth V9 that does this. In one sitting! With a better understanding that Firebase itself handles persistence of identification, the use of the “hook paradigm”, and application of “context”, this file above handles the “are they/aren’t they” logged in tracking anywhere in the site.
Originally, I had tried to over-engineer the way the site uses Firebase by putting all of my config, auth, and Firestore access into a class repo that could be called with minimal boilerplate. This time, of course, I adhere to the “one concern, one component” concept. A login system doesn’t need a globally available method. A user presence system doesn’t need a globally available method. A record listing doesn’t need a globally available method. Each of these examples should be self-contained components, so it makes sense to put as much heavy lifting into the component as possible. Once again, this makes each component a universe unto itself; our login component displayed above containing the email and password inputs and a single login button can be dropped in anywhere and will perform the exact same function without having to add extra code to the parent component to get it to work.
At this point I am more or less where I was at when I last worked on this project. My next step is going to be a doozy, however: I need to revisit the data store question. Firestore worked well, but the limitation of NoSQL still gives me anxiety. I am leaning towards MySQL, it being free and that I have it installed via Docker on my system, but that implies that eventually I’ll need to find hosting not just for the site, but for the database as well.