For video game releases you’re interested in, a useful feature might be the ability to create reminders for dates that are coming up.
Recently, I’ve been working on a Google Calendar integration to create events and set reminders for upcoming games. I have some things I need to spend more time on, but a happy path flow (with pictures!) is below:
Feature: ‘Google Calendar +’ button is displayed in the game detail card
When visiting the site for the first time, you need to authorize permissions for the app to access a personal google calendar
After authorization, if the call to create the event is successful, the button text + icon change and the button disabled
Opening up Google Calendar reveals that the event was successfully created!
The happy path is pretty straightforward, but there are some improvements and additional functionality I want to add to this over time.
Open questions – authentication + authorization
Pickle vs. JSON: Currently, I’m using pickle to serialize credentials, but it’s not human-readable (for a tradeoff of being very fast / efficient). I might switch to JSON even though it comes with a bit more overhead, so I can dig into issues should they arise in this flow (JSON is also widely quite supported so I might want to switch just for that)
Storing tokens: My python calendar service is looking for a credentials file before accessing the Google Calendar API. For more users, the service would have to fetch credentials + permissions for a specific user. In a production app, I would store and retrieve this info from a persistent database and probably avoid storing credentials in localstorage. I’ll likely implement something like this.
Additional event functionality
Customizing Google Calendar reminders: at the moment I am only using default fields, but there’s extensive customization for events
Knowing if a reminder is already set for a specific game: it would be helpful to know if I’ve already created a Google Calendar event reminder for a game. To do this, I could create a reference to the event using (after creation) and tie that set of events to a user. The edge cases could get complicated (ex. user creates event using the app, then deletes the event on google calendar), but I think it could be worthwhile.
Cleanup strategies for game releases that have already passed: it could be a small amount of data with only a few users, but maintaining that data could become expensive with a large set of users. A strategy to, for example, purge the app db references to created events after x period of time (6 months?) might be a way to save space over time.
Next, I think I’ll implement a user model (with PostgreSQL), and a sign-up flow, eventually coming back to build on some feature ideas I’ve laid out above.
I know this post wasn’t code heavy at all, but the next one will contain more code snippets. Thanks for reading.
Recently, I’ve been working on detail cards for individual games. In other words, creating a way to more meaningfully engage with the list of titles presented in the calendar view. Here’s a snippet of it in action:
This post will be split into a few sections:
Creating the detail popper
Cover art and data population
A popover to show hidden games
Creating the detail popper
As a user, I want to interact with a game listed on the calendar to get more information. My first instinct was to use a modal component, but I ultimately decided against this for two reasons: 1. I didn’t want to block scrolling to/clicking on other parts of the page, and 2. I wanted to avoid a component laying on top of the current view.
For context, a popper is a fancy wrapper component to position a popover or tooltip in the UI. The material UI library has a popper component I researched and built on top of for my use case.
To handle clicks on individual items on the calendar, the FullCalendar library exposes an eventClick prop. It comes with a prop, info, that has all of the data related to the event, as well as an HTML reference to the location of the click. Popper requires the anchorEl prop to determine where the pop-up should appear. Without it, the popper will just float in a corner of the screen.
The flow to create a popper on a calendar click event is as follows:
specify eventClick prop on Fullcalendar component
define a function with:
criteria for opening / closing the popper
the anchor element for the popper
any data necessary for the popper
When the open prop is true, the popper opens and data is passed to it. The next section talks about displaying data passed to the popper.
Cover art and data population
This is a close-up of the detail card in its current iteration. I have game cover art centered at the top, followed by information related to the game. There isn’t too much going on with this card as what is presented is read-only. The most interesting thing that’s going on is fetching the cover art (detailed below).
In the block above (starting with the useEffect hook), as the popper component is created, a loading spinner is displayed and placeholder image loaded as the cover art is fetched. Once loaded, the cover art replaces the placeholder. This usually happens in the blink of an eye, but occasionally you do see the loading spinner for the cover art.
A popover to show hidden games
I wanted a way to gracefully handle days where a ton of games are releasing. In the vanilla component, this is what that (less user-friendly) case looks like:
Ideally, I wanted a cutoff with a click to reveal the remainder of games. Fortunately, there are calendar props that allow you to control this, dayMaxEvents and dayMaxEventRows. If you set dayMaxEvents to true, the calendar will control the breakpoint for hiding further events based on window size. This is what the view looks like:
That’s progress, but after looking through the documentation, I couldn’t find a way to control the display when the view more button is clicked.
Above is what my solution looks like. My approach for this was to:
set dayMaxEvents to true
overriding the FullCalendar popover by passing ‘ ‘ to moreLinkClick
adding a click listener for show more games
inflating a custom popover
My click listener took some finessing, but the below gets the games based on the date and uses some js to only populate the hidden games.
useEffect(() => {
const getHiddenGames = event => {
if (event.target.className === 'fc-daygrid-more-link fc-more-link') {
// get and format the date
const displayedDay = event.target.offsetParent.offsetParent
.firstChild.textContent
const twoDigitDay = displayedDay.length === 1
? `0${displayedDay}` : displayedDay
const fullDate = calendarRef.current.calendar.currentData.viewTitle
.replace(' ', ` ${twoDigitDay}, `)
const formattedDate = moment(fullDate, "MMM DD, YYYY")
.format('YYYY-MM-DD');
// filter events for hidden games
const hiddenGames = events
.filter(entry => entry.start === formattedDate)
.sort(function(a, b) {return a.title.localeCompare(b.title)})
.slice(-parseInt(event.target.title.split(' ')[1]));
setSelectedDate(fullDate);
setHiddenGames(hiddenGames);
setHiddenGamesAnchorEl(event.target);
}
}
window.addEventListener('click', getHiddenGames);
return () => window.removeEventListener('click', getHiddenGames);
});
Within the link click event, I’m:
getting the day of the event click
ensuring that it is two digits long
placing the two-digit day into the calendar date (ex. ‘July 2023’ –> ‘July 06, 2023’
formatting the date (ex. ‘July 06, 2023’ –> ‘2023-07-06’)
filtering the total list of games by date, sorting that list in alphabetical order, and then returning only the portion of games that are hidden
I’m then passing that list of games to a popover component and using the existing popper component to display detail information.
Wrapping up, some random thoughts around this work
Positioning the popper element is an art (read: when it opens, sometimes content continues beyond the visible boundaries) I have yet to master
In an ideal world, I will be prioritizing the display of games based on ‘popularity’ or some other weighted feature. This would likely take significant time to develop, but I think would be a useful feature
I’ve had a lot of fun working on this so far. Next up for functionality, I think I’ll do some research into creating reminders to track upcoming games (google calendar, text message, etc.). Thanks for reading!
Starting off, I just want to say, it’s easy in the course of building something, to forget to celebrate the small wins. This blog entry is a reminder for myself to celebrate the small wins.
After figuring out how to make API requests with the IGDB API and format the response in JSON by date, my recent task has been to find a way to visualize the API response in a browser. Because I want to view them in monthly increments (as a proof of concept), I’ve been poking around for a react calendar component and happened upon FullCalendar.io.
Here is the result of the past week:
TL;DR: in the gif above, I’m clicking the next month button, which is sending a request to the IGDB API, displaying a loading spinner, and populating a list of video game releases for the next month (August, then September)
General process of how I got to the above (mostly front end stuff):
installed the fullcalendar npm package locally and implemented a vanilla component in a .js file
experimented to see how it would display a list of json events
used a json file with a list of video games for June
installed moment.js to perform some date conversions
mapped over game data to create a format that the react component recognized
created a listener for the next + previous buttons (so I could make API calls for game releases)
this took more time than I’d like to admit 😅
ended up creating a listener for a click event, updating a hook, and making an API request based on the hook value change
installed axios to facilitate the API request
I spent a few hours running into a 404 error with my axios requests. I thought the problem was with my server-side logic, but it turns out I was missing a front-end proxy to my django code! Oops, just glad it didn’t take more time
rendered react-spinner component while waiting for the API response, adding some formatting to make it look pretty-ish
implemented useEffect() hook to set the month on page load (with an empty dependency array) to make sure I was loading data for the current month
Observations + open questions
The API request + response process could be improved
Maybe I could implement a caching mechanism to reduce load time
Could also improve the python API request logic for added efficiency
Not sure how to scale authentication for lots of requests
the IGDB API requires auth to use (had to register my app with Twitch, the maintainers of the API) and while fine for an individual user, a bunch of users and a bunch of requests could really complicate the process
Need a way to prioritize display of popular games
there’s no inherent way to do that in the current iteration of the API (which they explain is a result of a subjective and changing definition of popularity)
maybe there’s a way to scrape several sites for anticipated upcoming releases and then aggregate +/ assign a weight to those games?
I still feel really inexperienced with Django (and python classes). Familiarity and comfort will come with time and experience, but I feel like I’m barely scratching the surface in terms of comprehension and functionality
What’s next
I have been meaning to get a private repo started (oops). Now that I have this much working locally, I think now is the time. It might also be time to get this running in some docker containers.
In terms of functionality, I think the next couple of things I’m curious to implement are filters for platforms and customizing the display of events in a given day.
Many years ago, when I was working on a team project in a coding bootcamp, I was responsible for handling login functionality (designing the front-end form, sending a login + token request, storing the access token in localstorage, authenticating those requests with the local token).
I barely knew what I was doing at that time, and though I got some things working, I wasn’t able to create a reliable implementation of a login flow. Since then, I’ve been a bit afraid of having signup + authentication be a part of my projects.
Fast forward to today. I’ve been using react for a few years here and there, but am much newer to python (and django). I’m resilient enough to keep pushing, so I wanted to create a web app with some basic features:
Signup functionality
Logged in / logged out experience (reflected with nav bar changes)
Verification email with redirect URL to site
Back end (django + simplejwt)
The basic flow of (this) authentication is:
client requests authorization using login credentials
server verifies credentials, returning a token if successful
client sets authorization header with the returned token for subsequent requests
when a token expires, a refresh token is used to generate another access token and refresh token pair
For authentication and creating a JSON web token, I used the djangorestframework-simplejwt python package. The basic steps include modifying settings.py and adding url paths to access token + refresh logic.
After these steps, I created a superuser, and tested using a combination of the admin portal (localhost:8000/admin) and postman (to set headers, etc.):
Hit ‘token/’ route using login credentials (in localhost:8000):
This should return an access token and refresh token:
Using the access and refresh values from the response, set appropriate variables in Postman (access token, header, and refresh token in the body).
After that, I hit a route to retrieve logged-in information to make sure that the authenticated endpoint works.
Front end (react)
The front end is made up of 3 main pieces:
Signup
Login
Logged-in view
This basic flow works as described below.
A user goes through a sign-up page to sign up for an account.
Upon form submission, the user is redirected to the login page with an account verification/activation email sent to the email from the signup form (see email implementation help in sources)
In an email, a user receives an activation link. When clicked, the link takes them to the site and ‘activates’ their account(in other words, is_active on the user model is turned to true)
Once an account is ‘activated’ the user can log in. When a user is logged in, they’re taken to a page where the navigation appears different (below).
The signup flow could be orchestrated in many ways, but in this project, I’ve chosen to explicitly turn off the is_active flag on the user model until the user clicks on the redirect link from the email.
A few things to call out
Local storage: so right now, I’m taking the response from the login call and setting a key-value pair for access + refresh token values. I’m changing the navigation bar options based on whether or not the access token is present (and using that token to authenticate subsequent requests). I also have an axios interceptor that checks for expired tokens and tries to access a ‘token/refresh’ endpoint to get a new set of tokens.
Activation redirects: in production, I’d likely use a combination of gunicorn and nginx to serve as a WSGI server + reverse proxy , but in my localhost example, I’m explicitly hardcoding the url. I had to do some finagling to add parameters to the url. Separately, as an additional feature, I could add in logic to handle cases where the link expires or the user somehow lost the first email (perhaps I will in the future).
Signup form validation: as of this writing, I don’t currently have validation on the signup inputs (username, email, and password). There are many libraries that handle this, but this wasn’t in scope for me to get signup + authentication up and running (though, if I decide to work further on this project, I’ll most likely build that in form validation)
Email server: I didn’t know this, but the django.core.mail package contains logic to fire off emails. It was pretty straightforward to configure and use! I was surprised how easy it was to configure, but glad I didn’t have to spend a long time figuring it out. There are a few links around emails in the sources if you’re interested.
Some final thoughts
I’m glad that I could get a straightforward implementation of a basic authentication flow up and running. I think I’ll incorporate elements of this project into other projects going forward.
Django comes with some pretty robust user creation and authentication packages out of the box. I thought a lot about creating my own user models or extending the existing user model. I put a couple of sources down below that helped me think about my options.