Prawf

Prawf is a Pauli Test simulation app made with Web Component and RxJS. You can find the reason why I made this in the project repo

Web Component

I chose Web Component because I want to try to experiment with it. I used it briefly in the past and I have no idea what I’m doing so I want to try to use it again.

Styling

I was going to use Tailwind but then I realised I use Web Component and I have no idea how to set it up. The second option is Open Props but then I thought it’s a bit overkill for what I’m doing.

Since I don’t use Web Component that much, there are probably some questionable things that I’ve done, and one of them is importing CSS as a string and then including it in the template.

I imported the css string using ?inline directive from Vite to import a file as a string. The result is… there are quite a lot of unnecessary whitespaces which makes the final HTML bigger. I’m sure I could probably minify it, but it’s fine for now.

One of the things that I like from Web Component is component isolation; meaning that other component won’t touch each other, they’re isolated. This is good and bad, the good is I don’t need to worry about accidentally touching other components, the bad is I duplicated a bunch of CSS because I couldn’t use utility classes.

Oh, I also added a darkmode with an animated toggle button because why not ツ It isn’t persisted, though.

Reactivity

Making something Reactive is very easy thanks to RxJS. I don’t think I struggle that much in this section. Again, I don’t use RxJS that much so there is definitely room for improvement.

For example, I re-render the entire component instead of modifying the node that I changed because I feel re-rendering the entire thing is a lot simpler and I don’t think it slows my app.

State Management

I use Svelte quite a bit, so I tried to make svelte/store-style state management using RxJS Subject. Here’s an example.

export let currentTheme: Theme = "light";
export const currentTheme$ = new Subject<ThemeState>();
currentTheme$.subscribe(({ current }) => {
  currentTheme = current;
});

I use a variable to persist the last state because I couldn’t find a way to “get” the last value from RxJS Subject.

Routing

I made a Subject to change the route. The way it works is actually pretty simple. Whenever I want to navigate to another page, I just need to emit a path as a string to the Subject and it will change the route using history.pushState.

Chart

I decided to build the chart myself instead of using a library since I only need two; which are lines and bars. I decided to make a smooth curved line. Thanks to this post that I found, I was able to make the chart.

History and Countdown

I used RxJS interval and timer for the countdown timer and just re-render every second. I used css transition for the top bar instead of re-rendering every time because I want it to animate smoothly when it gets shorter.

For the game history, I just pushed an object to a Subject. I also have a currentRound Subject that keeps getting updated by an interval Observable which tracks the current round.

Result Calculation

The result is calculated from the correct answers, incorrect answers, the percentage of the correct answer, answers per round in average, and the standard deviation.

I didn’t know how to decide a smoother chart (consistent) is better than a jagged chart (inconsistent). Fortunately, people at Teknologi Umum pointed me in the right direction, which was to find the standard deviation. It’s just simple math, apparently. I was expecting something more involved but no, it’s just a quick formula that could be done in less than a minute.