Building a Secret Santa Draw PWA in React and TypeScript
Over the past several years I have taken the opportunity of allocating Secret Santa’s for members of my family as a means of exploring new technologies. For the past several months I have been interested in the concept of Progress Web Application’s (thanks in large part to this series), and getting more hands-on experience in with the ever-changing frontend landscape. As such, I decided that this year I would build a small application that works across Desktop, iOS and Android to perform Secret Santa draws in both a local and remote setting. In this article I would like to document how I went about building this application.
The App
Based on the research and development carried out last month to build the Running Calculator, I had a good base (Vite, TypeScript, React and styled-components) to start from. The final build includes the ability to create Secret Santa draws in both a local and remote setting, using Redux to manage the application state. I wanted the application to be standalone, where-by all its behaviour could be deployed as a static artifact on GitHub Pages. I did not want to integrate it directly with an SMS provider like I have in past years, instead opting to use the Web Share API to craft a message with a unique URL to access each participants draw result. On top of this, I did not want a dependency on remote persistence (to store the draw results) so had to devise a means of passing the draw results within this shared URL.
Local draw
Local draws are carried out and presented on the same device. Once the draw has been completed, each allocation is presented one after another with the device being passed around to each requested participant until everyone knows who they are Secret Santa for. To ensure that participants do not accidentally see the next participants allocation I added a multistep process to reveal the result. Throughout development, I was able to sprinkle in several CSS animations to aid in the overall UX, one of which was fading in the recipients name upon being revealed.
Remote draw
Remote draws are carried out on a single device with the resulting allocations being shared with each participant via an encrypted URL (using the Web Share API). When the encrypted URL is visited the participant is presented with a similar screen to a Local draw, where they can reveal who they are Secret Santa for. Being encrypted (using the Web Crypto API) the user of the device who completed the draw (and tasked with sharing the results) is unable to snoop on each allocation. As all state about the allocation is stored with the URL there is no need for remote state persistence, and the application can remain a simple SPA deployed on GitHub Actions 🎉.
Draw history
Each draw is persisted locally on the device using the Web Storage API. This allows the user to retrieve the results again after the draw has taken place. Going forward it may be beneficial to review other means of persisting state on the Web i.e. IndexedDB. Currently, the draw results which are stored within the Redux state are subscribed to and persisted upon each store change. This is a little unperformant as we are required to encode and write JSON entities anytime the store changes, but this will suffice for the initial release.
Snowfall
There is a small easter egg on the applications home screen. Using the DeviceMotionEvent when you shake the device, the amount and speed of the snow flawing increases. On iOS the user is required to given permission to the application to read this information, as such, they are required to click the small bottom-right phone icon (if it appears) before shaking the device.
Conclusion
Like previous years I have enjoyed exploring another means of taking the problem of allocating Secret Santa and applying it to a project to learn new technologies. This build has expanded my use of what is possible within a Progress Web Application using many more Web APIs that are present. Over the last day or so I have been looking at the possibility of packaging this PWA up into a native iOS and Android application using Capacitor. I hope to find some time to release this onto the Google Play and App Store and document my experience in doing so in the future.