Development Update #2: Ratings, Subscriptions and Profiles
Repository
https://github.com/jrawsthorne/review.app
New Features
Better filters for pages - store list of posts matching filter in redux storePost formatting - youtube/dtube/more complex markdown/htmlUser profile pagesAllow all users to give star ratingSubscribe to certain showSubscribed 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.
#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.
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' }));
});
#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>
}
Roadmap
- Ability to actually write new posts
- Store all database info in JSON metadata of a post as backup
- User actions (like, comment)
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