Skip to content

Instantly share code, notes, and snippets.

@btjm123
Last active November 23, 2023 21:12
Show Gist options
  • Select an option

  • Save btjm123/006376c2ea65a68f3251bc7c9a33ca79 to your computer and use it in GitHub Desktop.

Select an option

Save btjm123/006376c2ea65a68f3251bc7c9a33ca79 to your computer and use it in GitHub Desktop.
Software Engineer Intern @ Zenith

Zendora

Welcome to Zendora! This is a cross-platform React Native app designed to gamify and enrich students' learning experience through engaging quizzes, on-demand video lessons and competition amongst peers. I no longer have access to the main codebase but I have documented my sailent learning points onto this document for future reference.

exploration

Prerequisites

Before you begin, ensure that you have the following software installed on your local machine:

About the project

This project was concocted by an engineering team of 10. I took charge of implementing several pivotal features that greatly enhanced the app's functionality, namely the virtual whiteboard, the rewards carousel and lastly the on-demand video lessons.

Virtual Whiteboard

whiteboard

This was probably one of the most technically challenging task. The initial whiteboard implementation was easy enough - we stored a global state (i.e an array) consisting of our own in-house SVGPaths class wrappers (i.e IPath). Each IPath element contains path-specific contextual information, such as x/y coordinates, stroke width, stroke colour etc. Each time the user taps down and drags his finger across the screen, we re-render the state of the whiteboard in real-time. Though this was not very performance-efficient - since we were re-rendering all the IPath elements that were already on the screen.

whiteboard1

To optimize this, we segment our whiteboard into 2 distinct x-y planes. The bottom x-y plane is knwon as the Display Layer, so it will simply display all the IPath elements that has been drawn before. The top x-y plane is knwown as the Drawing Layer, so this is the layer that the user will actually draw on. Now, whenever the user drags his finger across the screen, only the Drawing Layer will be re-rendered in real-time. The other paths that were previosuly drawn remain intact and static on the Display Layer. Only when the user lifts up his finger, the path rendered will be "transferred" onto the Display Layer. This reduces the amount of unneccesary re-renders, hence optimizing performance.

whiteboard2

Another challenging part of this task was how do we persist the user canvas in our backend. The first approach we conjured was to get a snapshot of the whiteboard canvasm, export it to an image (i.e maybe a Base64 string encoding) and save in our Amazon S3 (since image storage was relatively cheap). However, one drawback of this was that we would lose the whiteboard state (i.e the IPaths[] context) and the user wouldnt be able to amend/erase existing paths from the canvas. Another approach consists of stringifying the whiteboard state (i.e IPaths[]) and writing the string as a entry into our PosgreSQL database. So whenever the user re-opens the question, the state of the whiteboard will be restored and the user will be able to resume drawing at where he/she left off. In the end, we went with both approaches, since we also needed to support the functionality that teachers should be able to mark over the students' canvas submissions (i.e like a physical homework).

Video-In-Demand Lessons

lesson-video

Probably my second most challenging task. While there were libaries that provided a simple media player interface, one of the product requirements was that the seekbar must be not be seekable. On top of that, the interface had to support a double-tap-rewind feature (similiar to Youtube), which none of the libaries supported. To that end, I had to create a custom video player inferface component, with at least 10 different states, each tracking a specific piece of contextual information.

Also, notice the little green markers along the seekbar? When the video streams to the corresponding green marker, a lesson question modal (which was to be rendered with RichText to support rendering of Latex mathematical equations, images etc.) has to be shown to validate the student's understanding of the concepts explained in the video. I learnt a lot about race conditions, state management and importance of technical documentation in this task.

Screenshot 2023-11-24 at 4 18 06 AM

The integration with the backend was not any easier. I had to save the state of where the user left off (so that the user will be able to resume watching), and keep track of the total time the user spent watching the video in a single session. This data, amongst many others was later ingested to churn out meaningful student data analytics, to give parents (and students themselves) insights into their learning habits.

But to give credit where it's due, the libaries that I've used already supported streaming and buffering out of the box, so it made implementation of so-called "own" video libary a lot less painful than it could be.

Rewards Carousel

gacha-carousel

This was a very frontend-heavy task that I tackled. Luckily there were libraries such as react-native-carousel that made things a lot easier for me. Though this task was particularly frustating for me as I had to adjust and fine-tune the numbers such that the snap effect on the carousell appear smooth and slick.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment