Over the last couple of months I started writing a browser-based Spotify interface that fits the way I use the platform.
I’m sharing it here to gather some feedback around structure and patterns.
So far I have one main view (the explorer) which mounts a component (the bottom mini player) which mounts another component (the song progress bar). This hierarchy is the result of experimenting with the aim to minimize the amount of data sent over the network.
ExplorerLive |_ MiniPlayerComponent |_ ProgressBarComponent
Specifically: the “currently playing status” is updated every second according to these rules:
- A song change concerns the entire UI, as the currently playing song is highlighted in a few different places (e.g. if you’re browsing the album that contains the song).
- Playing, pausing, volume or device changes should update the entire miniplayer
- Progress in the same song only updates the progress bar
Implementation-wise, this translates to:
- and 2. Update relevant assigns in ExplorerLive and re-render the entire central portion of the UI and miniplayer
- Send updates to ProgressBarComponent and re-render the progress bar
Components are stateful, but not isolated process-wise.
Things I’m unsure about:
- For case 3. the live view directly addresses the progress bar, bypassing the mini player component. This works, but I do wonder if it’s a case of implementation leak.
- All event handling is in the live view. Again, seems like an implementation leak.
- I’m finding directly managing assigns error-prone: I’d like to use less keys and have specialized functions to update the assigns state, but that makes it more difficult to control diffing during updates (i.e. more UI is re-rendered).
Session state machine
I’ve implemented the main connected user session as a separate state machine.
The implementation takes care of managing initial authentication, refreshing credentials, periodically polling new information and auto-termination in case a user has disconnected.
It’s implemented outside the live view session so that a user can have multiple browser windows open with only one session (and only one polling lifecycle).
I’m fairly happy about the structure so far - once you know the rules behind events in
gen_statem, it looks reasonable to me but I’d like a second opinion. The code documentation has a diagram/description on how it’s implemented.
Here’s where I’m doubtful (lifted from the module docs)
Broadcast and subscribe are implemented via
Phoenix.PubSub, however the
Worker maintains its own set of monitored processes subscribed to the session
Subscription tracking is necessary to implementing automatic termination of a
worker after a period of inactivity. Without that, the worker would
indefinitely poll the Spotify API, even when no client is interested into the
topic, until a crash error or a node reboot.
In practice, a live view session subscribes to the worker process. The worker monitors the live view session, so that it can decide if it’s safe to terminate. Two browser windows for the same user equal two monitors.
Having a custom monitoring logic makes it that
Phoenix.PubSub is almost redundant. I could message those processes directly, plus in a distributed scenario I would still need to update the state machine work correctly.
I’m experimenting with tests seeded with stream_data and expressed as properties. This suits well specific sections of the UI, e.g. search, where having randomized search results already helped me find issues I hadn’t thought about.
Properties, however, slow down (non-linearly) when I increase the number of runs. I still have to investigate and see where the problem is (e.g. shrinking gets slower or interaction with Mox is bottlenecked) - I’m wondering if by looking at the tests structure anyone with more experience in combining stream_data and Mox can shed some light. If not, I’ll dig and report back what I find.
- You can use the application at tune.fullyforged.com (with no uptime guarantees, but it’s my production instance).
- Source at https://github.com/fully-forged/tune
- Code documentation at https://tune-docs.fullyforged.com
Thanks for reading so far!