•February 28th 2020
Stream provides a useful tool to generate user tokens to authenticate and limit their ability to modify data on the service. We will first have to generate these tokens on our backend and pass them to the user through a template. Much like our previous interactions with Stream, we will create user tokens as a method on the
User class (in
app/models.py) to be returned in a request.
Now that we have a way to generate tokens for the
user, we will return them in the views that require them. In the last couple of articles, we made ways for a user to create, update, and delete collections and content, but no way for them to be able to view it. As collections are the highest level component created by a user, we will create a feed on the
user page to view all of the collections that they’ve created. After that, the
collection page will have a feed of all the content that has been added to it. Let’s update
app/main/views.py to generate the Stream tokens for both and return them in their respective views:
Next, we’ll make the same adjustment for the
base.html file. While we are there, we can also update our navbar to add routes for adding collections and content:
Templates in Templates
Users expect a visual consistency in accessing elements across a site, and you want to try and modularize components as much as possible to avoid repeating yourself (DRY!). To employ this philosophy for our app, we will create a template for rendering collections and content and simply import them to the pages where we will be using them. That way, if we make a change to the layout of a component, it will be replicated across the entire site, rather than having to recode every single page.
content share a number of attributes, so the layout will be rather similar between them:
As compared to
Rather than paginating users’ collections with an ugly set of buttons at the bottom, we want to use an “infinite scroll” feature. Infinite scroll has become a rather ubiquitous feature in modern social media sites, and I find it makes the user experience a lot more fluid than continuously clicking to navigate through pages. Infinite scroll is actually a surprisingly simple feature to implement, when you know how to do it; with that said, there are a few “best practices” that can often slip through the development cracks. For the HTML portion, it's pretty simple. There’s the template that we just defined, a scroller to which newly formed elements are attached, and a sentinel element, which monitors for when a user (
app/templates/user.html) has scrolled to a specific threshold of the screen to request and render new elements to the DOM:
Following the Script
Next, we will set up a script to request and render the information on the page in our
First, when the DOM content is loaded, we create a Stream client using a public key and the token you created and passed through the view. After that, we create a variable to hold the feed of the page that we are currently viewing.
Next, we create variables for the template, scroller, and sentinel, as well as one for whether or not a collection is currently loading as a boolean value and the last ID of the most recent collection that was retrieved. As you can see at the beginning of the
loadCollection() function, it first checks to make sure that the script isn’t already running to make sure we don’t render duplicates. The next step is checking to see if there is a
last_id value, and if there is, including it with the
user.get() function. Stream recommends you use
last_id as the preferred method of pagination, as it drastically reduces the latency of the requests. Once the data is requested, it is checked to ensure there are values present in the results and, if not, the app returns a message to indicate the end to the user.
Once a response is returned with results, the script loops through it and, for each of the results, populates the template fields, appending the completed template to the scroller. Finally, it creates a new intersection observer on the sentinel component for the next time a user scrolls to that location. For the timestamp, we add a “Z” to the end of the string, to make sure that it is parsed properly for UTC with
Once again, we are going to replicate the same steps for the collections page in rendering content. First, we insert the template, scroller, and sentinel in the
Our user page should now render any collections a user has at the bottom of the page in an infinite scroll feed:
Once you’ve created your first collection, you can then click through the title to get to the collection page and see any content rendered at the bottom, in the same way you can see a user’s collections at the bottom of their page:
Congratulations! We have now created an infinite scroll element on both
collection pages: a way for registered users to create collections and content, as well as easily navigate the site and all of its elements. In our next article, we are going to get into allowing users to follow each other and specific collections, before creating a homepage timeline that aggregates all of the activity from each user’s follows into a customized feed!
As always, thanks for reading, and happy coding!
Note: The next post in this series can be found here.