Implement a simple endpoint to return a static custom screen

ℹ️ This article is Part 3 of Custom screens - Server side rendering.

 

Overview

Time to finally implement a simple custom screen!

In this article we will learn how to build a simple screen that just displays the tips earned by the rider of a company that does instant delivery.

 

Requirements

Before anything else, let’s lay out the requirements of what we need to achieve. Given a logged in rider in the Driver app, we want the following:

  • A new custom screen called 'Tips' is available in the side menu
  • When opened, it displays the tips earned so far by the rider grouped by time interval
  • We support 4 time-intervals: 'yesterday', 'current week', 'current month', 'current year'
  • For each interval we show a title that represents the interval name and the tips + total orders from that interval in the following format:
<tips in USD> - <nr of deliveries>

 

Design

Frame_1_2_.png

 

Project setup

The showcase project is going to use NodeJs, Express & Typescript, but feel free to use anything you’re comfortable with. We’ll use the jsonwebtoken nodejs library to verify the authenticity of requests coming from the MotionTools server to ours.

To test without deploying your app, you can tunnel your local development server using ngrok (run ngrok http 3000) once your app is started. This provides you with a temporary base-url accessible via the internet, e.g: https://zbd7-2-5-163-184.eu.ngrok.io.

Bear in mind this ngrok URL changes every time you restart ngrok, but for development purposes it should be fine. The full path of the endpoint that will build the custom screen payload is <base_url>/riderTips.

 

Implementation

Given the designs, we’ll use the Labeled box component to render our custom screen.

In the end, the payload we return needs to look in the following way:

{
    "title": "Tips",
    "components": [
        {
            "component_type": "labeled_box",
            "title": "Tips Yesterday",
            "content": "$62 - 28 deliveries"
        },
        {
            "component_type": "labeled_box",
            "title": "Tips this week",
            "content": "$242 - 112 deliveries"
        },
        {
            "component_type": "labeled_box",
            "title": "Tips this month",
            "content": "$417 - 211 deliveries"
        },
        {
            "component_type": "labeled_box",
            "title": "Tips so far this year",
            "content": "$4372 - 2017 deliveries"
        }
    ]
}

 

Adding the custom screen in the dashboard

First, we need to go through the steps here, with the following mentions:

  • Set the title to: 'Tips'
  • Set the URL to <ngrok_url>/driverTips. In our case it would be: https://zbd7-2-5-163-184.eu.ngrok.io/driverTips
    • Bear in mind you will have a different ngrok URL!
  • Save the created Public Key as an environmental variable called JWT_TOKEN_PUBLIC_KEY

 

Implementing the endpoint

Let’s start by adding an empty POST <base_url>/driverTips method to our project:

rootRouter.post("/driverTips",
    async (req: Request, res: Response) => {
	//TODO
    })

When we receive a request to this endpoint, we should first verify that the request is authentic and from MotionTools. To do so, we perform the verification described in the previous article (here) — we’ll use the jsonwebtoken library for this:

rootRouter.post("/driverTips",
    async (req: Request, res: Response) => {

        //the JWT public key we saved in our env variables is BASE64-Encoded. First step is to decode it
        const decodedJwtPublicKey = Buffer.from(process.env.JWT_TOKEN_PUBLIC_KEY, "base64").toString("ascii")

        //obtain the auth token from the header
        const authHeader = req.header("Authorization").replace("Bearer ", "")

        //verify + decode the JWT token. if verification is successful, we'll obtain the payload of the JWT
        const jwtContents = jwt.verify(authHeader, decodedJwtPublicKey, {
            algorithms: ["ES512"]
        })
				
	//the motiontools id of the rider that requests to open the custom screen is present in the "sub" field.
        const riderId = jwtContents.sub

        ...
    })

We won’t detail further how the verification works, as it should be already covered by the inline comments & in the previous articles. Perhaps relevant for this step is also the Introduction to JSON web tokens article on jwt.io.

Now that we have verified that the incoming request is authentic, and now that we have the MotionTools userId of the target rider, we’re ready to finally build the payload to render the custom screen:

rootRouter.post("/driverTips",
    async (req: Request, res: Response) => {

        //decoding the JWT token. removed for brevity
				...

				const riderId = //obtained, see previous section
        const components: Array = []

        components.push({
            component_type: "labeled_box",
            title: "Tips Yesterday",
            content: calculateTipsForInterval(userId, "Yesterday")
        })
        components.push({
            component_type: "labeled_box",
            title: "Tips this week",
            content: calculateTipsForInterval(userId, "This week")
        })
        components.push({
            component_type: "labeled_box",
            title: "Tips this month",
            content: calculateTipsForInterval(userId, "This month")
        })
        components.push({
            component_type: "labeled_box",
            title: "Tips so far this year",
            content: calculateTipsForInterval(userId, "This year")
        })

        return res.status(200).json({
            title: "Tips",
            components: components
        })
    })

As you can see, what happens above is quite straightforward. We first initialize an empty array:

        const components: Array<any> = []

Following that, we populate the array with 4 objects that match the schema of a Labeled box component.

        components.push({
            component_type: "labeled_box",
            title: "Tips Yesterday",
            content: calculateTipsForInterval(userId, "Yesterday")
        })

The calculateTipsForInterval(userId, "Yesterday") method calculates the tips for a particular userId & time interval combination. It’s implementation was left out to keep things short here, but feel free to implement it on your own using the MotionTools API!

At the very end, we embed our components array into an object whose schema matched the schema for a custom screen.

This is also where we add the title & finally return the payload:

        return res.status(200).json({
            title: "Tips",
            components: components
        })

 

That’s it! Time now to open the Driver App and then navigate to the newly created 'Tips' custom screen! 🎉

 

Final notes

Implementing a custom screen in MotionTools seems like a major endeavor but once familiar with the process, implementing complex custom screen flows should be a breeze! Before we close off, please take the following into consideration:

  • Consider the important notes from Part 1, especially the point about having to issue a response within 1 second from receiving an incoming request.
  • Although not showcased in this article, we encourage you to localize the strings present in your custom screen, otherwise it could result in a bad experience for your users — the UI of the MotionTools app might be in one language whereas the custom screen would be in a different language.
  • As a particular custom screen flow becomes more and more complex, the endpoint that’s called when your custom screen is opened from the side menu will take more and more responsibilities. Besides rendering this will also include navigation (back navigation as well via previous_page_parameters) between your various screens. At that point it might make sense for your method to act more as a router that just identifies the right child-method to handle a specific request.

In closing, we want to encourage you to play with our existing components, we’re eager to see you innovate! And of course, if you have an idea about a new component, please do not hesitate to contact us at support@motiontools.com.

⤴️  Back to Overview

Was this article helpful?
0 out of 0 found this helpful

Articles in this section