Development Update #2: Ratings, Subscriptions and Profiles

in #utopian-io6 years ago

Repository

https://github.com/jrawsthorne/review.app

New Features

  • Better filters for pages - store list of posts matching filter in redux store
  • Post formatting - youtube/dtube/more complex markdown/html
  • User profile pages
  • Allow all users to give star rating
  • Subscribe to certain show
  • Subscribed page showing all posts from subscribed shows/movies

#1 User profile pages GitHub Link

You can visit a user's profile page by going to /@username like with most steem apps. The profile page shows the name, username, website, location and bio at the top with the avatar to the left and cover in the background. Below that is posts by that user.

image.png

profiles.gif

#2 Star ratings

I decided that a user should be able to rate any media item on the site so I needed to create a place for ratings in the database.

This is the schema (GitHub Link)

const schema = new Schema(
  {
    user: {
      type: Schema.Types.ObjectId,
      ref: 'users',
      required: true,
    },
    score: {
      type: Number,
      required: true,
    },
    mediaType: {
      type: String,
      required: true,
    },
    tmdbid: {
      type: Number,
      required: true,
    },
    seasonNum: {
      type: Number,
    },
    episodeNum: {
      type: Number,
    },
  },
  {
    timestamps: true,
  },
);

Once logged in a user can go to any post and with the rest of the metadata at the top there are 5 stars. Clicking one will immediately add this rating to your account. In the future when there are sufficient ratings I hope to display an average as well. Clicking the same number of stars again will remove the rating so the minimum you can give is 1 star.

image.png

ratings.gif

When a user sets the score to 0 it is completely deleted from the database. GitHub Link

if (parseInt(value, 10) === 0) {
          return Rating
            .findOne({
              user,
              mediaType,
              tmdbid,
              seasonNum,
              episodeNum,
            })
            .remove()
            .then(() => {
              User.update({ _id: user.id }, { $pull: { ratings: rating.id } })
                .then(() => res.json({}));
            });
        }

#3 Subscriptions

A user is also able to subscribe to certain shows or movies if they want to produce a feed with just posts from those shows/movies. I decided to implement this and will provide it as well as a general steem feed of content from users you follow.

This is the schema (GitHub Link)

const schema = new Schema(
  {
    user: {
      type: Schema.Types.ObjectId,
      ref: 'users',
      required: true,
    },
    tmdbid: {
      type: Number,
      required: true,
    },
    type: {
      type: String,
      required: true,
    },
  },
);

To display all the posts for a subscription I loop through each one and find all the posts matching that criteria and add it to an array. (GithHub Link)

router.get('/subscriptions', passport.authenticate('jwt', { session: false }), (req, res) => {
  const { user } = req;
  let count = 0;
  Subscription.find({ user })
    .then((subscriptions) => {
      Promise.all(subscriptions.map(subscription =>
        Post.count({ type: subscription.type, tmdbid: subscription.tmdbid }).then(c =>
          Post.find({ type: subscription.type, tmdbid: subscription.tmdbid })
            .then((posts) => { count += c; return posts; }))))
        .then(posts => res.json({
          count,
          results: orderBy(flatten(posts), 'createdAt', 'desc'),
        }));
    })
    .catch(() => res.status(404).json({ error: 'Error fetching subscriptions' }));
});

image.png

image.png

subscriptions.gif

#4 JSON web token

To facilitate the previous two new features I added a new sign in flow so that I don't have to rely on steem authentication for every protected action. Having to access the steem api for every rating and subscription was very slow. As those two things don't rely on the steem blockchain it was unnecessary to authenticate using a steemconnect token so I now store a JSON web token with the users details in it. (GitHub Link)

router.post('/login', (req, res) => {
  const { accessToken } = req.body;
  if (!accessToken) res.status(400).json({ error: 'Not authenticated' });
  steemConnectAPI.setAccessToken(accessToken);
  steemConnectAPI.me()
    .then((steemUser) => {
      User.findOne({ username: steemUser.name }).populate({ path: 'ratings', select: 'score mediaType tmdbid seasonNum episodeNum' }).populate('subscriptions', 'tmdbid type')
        .then((user) => {
          if (user) {
            const token = jwt.sign({
              id: user.id,
              username: user.username,
            }, process.env.JWT_SECRET, { expiresIn: '1h' });
            res.json({
              user: {
                ...steemUser,
                account: {
                  ...steemUser.account,
                  ratings: {
                    scores: user.ratings,
                  },
                  subscriptions: {
                    items: user.subscriptions,
                  },
                },
              },
              token,
            });
          } else {
            new User({ username: steemUser.name }).save().then((newUser) => {
              const token = jwt.sign({
                id: newUser.id,
                username: newUser.username,
              }, process.env.JWT_SECRET, { expiresIn: '1h' });
              res.json({
                user: steemUser,
                token,
              });
            });
          }
        });
    })
    .catch(() => res.status(400).json({ error: 'Not authenticated' }));
});

#5 Updated styling

The interface for switching between episodes and seasons has been improved with a clickable tooltip. Although this doesn't provide as much information for the user, it saves a lot of space that was wasted before in my opinion. (GitHub Link)

{props.seasons &&
  <div className="MediaHeader__info__selectors">
    {props.seasons &&
      <Popover
        onVisibleChange={props.handleSeasonVisibleChange}
        visible={props.showSeasons}
        placement="bottom"
        content={Object.values(props.seasons).map(season =>
      (
        <p
          onClick={() => handleSeasonClick(season.season_number)}
          className="Filter__option"
          key={`season-${season.season_number}`}
        >{season.name}
        </p>
      ))}
        trigger="click"
      >
        <span className="Filter__dropdown" style={{ marginLeft: 0 }}>
      Seasons <Icon type="down" style={{ fontSize: 15 }} />
        </span>
      </Popover>
    }
    {props.episodes &&
      <Popover
        onVisibleChange={props.handleEpisodeVisibleChange}
        visible={props.showEpisodes}
        placement="bottom"
        content={Object.values(props.episodes).map(episode =>
      (
        <p
          onClick={() => handleEpisodeClick(episode.episode_number)}
          className="Filter__option"
          key={`season-${episode.episode_number}`}
        >{episode.name}
        </p>
      ))}
        trigger="click"
      >
        <span className="Filter__dropdown">
      Episodes <Icon type="down" style={{ fontSize: 15 }} />
        </span>
      </Popover>
    }
  </div>
}

image.png

selectors.gif

Roadmap

  • Ability to actually write new posts
  • Store all database info in JSON metadata of a post as backup
  • User actions (like, comment)

Proof of Work Done https://github.com/jrawsthorne

Sort:  

Hey @jrawsthorne
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Thanks for the contribution!

Looking good, it's coming along nicely! For future contributions it would be nice if you linked to a PR or all the relevant commits directly - makes for easier reviewing, but it's not required of course.

Click here to see how your contribution was evaluated.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

I'll be sure to do that in the future, thanks. I'll comment everything as well, not sure how what slipped my mind.

Congratulations @jrawsthorne! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes received

Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

Upvote this notification to help all Steemit users. Learn why here!