
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
Complete source code for this lesson is available at
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 Before getting started with integrating votes, we first have to show the total number of upvotes and downvotes right here,
00:00:09 alongside showing whether the currently logged in user has upvoted, downvoted, or done nothing.
00:00:15 Depending on user's action, we have to change the color of the icon.
00:00:19 If it has already been upvoted, it'll be green, and if it has already been downvoted, it should be red.
00:00:24 To get this information of whether a user has upvoted or downvoted, we need to make a call to the vote model, which stores votes for different users for
00:00:33 that type, such as a question or an answer.
00:00:35 We can either retrieve all of this information right from the getQuestion action.
00:00:41 I'm talking about this one right here.
00:00:43 Or we can create a separate call.
00:00:45 Let's call it hasVoted that returns just the info on whether the user has voted or not.
00:00:50 Choosing which is best depends on the trade-offs between performance, scalability, and code maintainability.
00:00:57 If your app is small and has a manageable number of votes, using a single API call such as getQuestions is more efficient.
00:01:06 It reduces network requests, keeping question and vote data in sync, and simplifies front-end handling.
00:01:12 But it is also blocking as you're trying to get lots of data in a single call.
00:01:16 But if you use two separate calls, that improves performance and scalability since the user doesn't have to wait for all of the data to load at once.
00:01:25 This approach is non-blocking, as you can display a loader while fetching votes and show the question details immediately.
00:01:32 This not only enhances performance, but also improves the user experience.
00:01:36 So, since that is a better approach in this case, I'll teach you how to do just that.
00:01:41 Let's create a separate action that returns the information on whether the user has upvoted or downvoted a question or an answer.
00:01:48 As usual, we can first do the validations by heading over into the validations.ts file.
00:01:54 And at the bottom, we can say export const.
00:01:58 hasVotedSchema and make it equal to createVoteSchema.pick.
00:02:07 This is an interesting one.
00:02:08 I don't think we used pick before.
00:02:10 What pick allows us to do is to choose only some of the properties from a previously defined schema.
00:02:16 So if we had a recipe with an ID, name, and ingredients, and if you want to keep just the name, you would call the recipe and call the .pick with the name
00:02:25 of true.
00:02:25 In our case, that'll look something like this.
00:02:28 From the create vote schema, we want to pick the target ID and the target type.
00:02:33 In this case, we don't care whether it's an up vote or a down vote.
00:02:36 I'll soon explain why.
00:02:38 And we can also do the same thing for the action.d.ts.
00:02:43 If we scroll to the bottom, where we can define a type has voted params and make it equal to a pick between create vote params and specifically want to
00:02:57 pick the target ID or the target type.
00:03:01 And we can also create an interface, which is going to be has voted response, which will be equal to has voted.
00:03:09 or rather has upvoted which will be of a type boolean and has downvoted also of a type boolean, true or false.
00:03:17 And finally we can head over into the vote.actions.ts file and we have the update vote count as well as the create vote but now we can create another function
00:03:30 export async function hasVoted which will take in the params of a type hasVoted params and it'll return a promise of an action response.
00:03:45 specifically that will return a hasVoted response.
00:03:51 And we can close it like this.
00:03:53 So here, we can get access to our typical validation result, just to make sure that we're properly passing everything we need to pass into it.
00:04:00 That'll be equal to the await call of the action utility function.
00:04:05 We need to pass in the params, the schema of hasVoted schema, coming from validations, as well as authorized true, because we have to be authorized to
00:04:16 be able to upvote or downvote.
00:04:19 Next, we can check if a validation result is an instance of error.
00:04:26 We can just return handle error, validation error as error response.
00:04:30 You know the drill.
00:04:32 But if we don't have an error, we can extract the target ID as well as the target type.
00:04:39 from validation result dot params.
00:04:44 And we can add an exclamation mark saying that we know it'll be there.
00:04:47 Since we're using the authorized true, we can also extract the user ID coming from validation result dot session question mark dot user dot ID.
00:04:56 Then we can open up a try and catch block.
00:05:00 In the catch, we can return a handle error as error response.
00:05:04 And in the try, we can get access to the vote.
00:05:08 by saying await, vote, we're referring to the vote model right here, dot find one, and we want to find a vote belonging to our specific author.
00:05:20 So author is equal to user ID, where the action ID is equal to the target ID, which means on that specific question or that specific answer.
00:05:30 So we also need to pass the action type, either upvote or downvote.
00:05:35 Next, if there is no vote, we can just return an object that says success is false.
00:05:43 And data will be equal to an object where hasUpVoted is false and hasDownVoted will also be set to false.
00:05:52 But if we do have a vote, so that'll be outside of this if, so let me wrap this if properly.
00:05:59 If no vote, then return this.
00:06:01 Else, we can simply return a success of true, and we can set a data of hasUpvoted.
00:06:10 If vote.voteType is triple equal to upvote, then by default, this will be set to a boolean true.
00:06:17 Similarly, if vote.voteType has been set to downvote, then this will be true.
00:06:22 So only one of these can be true at one time.
00:06:25 And that's it.
00:06:25 This is sending that Boolean value of true or false because the result of a comparison is always a Boolean.
00:06:32 And in the next lesson, I'll teach you how to integrate this new hasVoted action we have created.
00:06:38 So let's go ahead and commit this.
00:06:41 I'll call it implement hasVoted, commit and sync, and let's put it to use in the next lesson.