English

TL;DR

I didn't think it would take almost three days to make this alone, but it's all from scratch. JUST make the reactions feature, yes that's what follows below ๐Ÿ˜

Don't do the analysis first, just make the database, trial & error and refactoring. Some of them are just lazy, but the one who clearly wants to code immediately ๐Ÿ˜‚

Once in this article, I'll explain what the features are, how to make them, but it's not a step-by-step tutorial.

Who knows, some of you want to make it too, right, so you can cheat a little bit, but just take the 'good'.

Gasss right away...


Features

First of all, I thought about the rough picture I wanted to make like this reaction.

And here it is, roughly the features are like this:

  • Multiple Reactions

    There are claps, wow etc. and as easy as possible if for example in the future you want to add another reaction.


  • Batch Reactions

    Can give a similar reaction several times, for example 20x claps, 10x wow etc.


  • Section Reactions

    You can know where the user gives a reaction, for example giving 10x claps in the section titled 'Features' like this section.



Concept

So actually it took a long time to create a REST API, a lot of refactoring, a long time to create the database schema and other things that are towards BE.

There must be a lot of things that are not accurate, please be informed, it's not BE's child that's the problem โœŒ

Database

To be honest, after three years like that, I just dealt with the database again. Last time I took care of the database when making the application for the final exam of the lecture.

First, decide where you want to host. The SQL noSQL problem is not a problem, because it is still a trial and error, so try to find a free one and get MongoDB Atlas shared hosting, it's pretty good.

Because I also use prisma for the ORM, so it's not too complicated to migrate to another database.

Let's go to the database schema. This is only focused on the reactions feature, there is actually a field for views and shares but I deliberately did not include it.

First, I created the ContentMeta table. This is to save the post data.

It is necessary to save the title, date, content, etc. because the post data is built locally from mdx, so just save the slug.

model ContentMeta {
  id          String

  slug        String
  createdAt   DateTime
}

Then this is the important thing, the Reaction table, to save the reactions.

Reaction model {
  String id

  type ReactionType
  String section
  count Int
  sessionId String
  createdAt DateTime

  contentId String
}

enum ReactionType {
  CLAPPING
  THINKING
  AMAZED
}

Let's break it down slowly:

  • type

    It is very important, because it uses multiple reaction, this type' must be used. I use the predefined enum ReactionTypefor the data type, so the contents can only be betweenCLAPPING, THINKING, and AMAZED`.

    If you want to add another reaction, you can add a new item to the enum. Another option is to change the data type to String, so for example if you want to use the reaction all emojis can also be used.

    Why not create a relation to the new table?

    It's possible, but I think it's going to be complicated later on in the query, using an enum is also enough, everyone should be careful with type safety too โœŒ


  • section

    Just save the title of the section that is active when the user clicks on the reaction. For example, when the user reads the title 'Database', then this section will contain 'database'.

    Just run the IntersectionObserver, observe all existing section titles, the last one 'isIntersecting' is used as the value of this section .


  • count

    This makes the total batch reaction. So when you click the reaction, it doesn't actually send directly to the API, but it is delayed for a few milliseconds, debounce that is.

    It's as simple as this: after clicking the reaction several times in a short period of time, the number of clicks is first collected (for example 10 clicks), the results are immediately sent to the API, and the number of clicks is stored in this count.

    For me, this method makes more sense, than requesting 10x API and making 10 row reactions with the same data. Let's save the database storage.


  • sessionId

    Save 'who' is reacting. Not the user's name or identity, but I use the user's ip address.

    Eits, don't save the ip address either, because this includes very sensitive data for the user.

    I hashed the ip address, plus 'digaremin' as well so it can be fixed hehe. So no one knows the user's ip address, including me who created it.

    The code looks like this:

    export const getSessionId = (req: NextApiRequest) => {
      const ipAddress = req.headers['x-forwarded-for'] || 'localhost';
    
      // hashes the user's IP address and combines it with
      // a salt to create a unique session ID that preserves
      // the user's privacy by obscuring their IP address.
      const currentSessionId = createHash('md5')
        .update(ipAddress + process.env.SALT_IP_ADDRESS, 'utf-8')
        .digest('hex');
    
      return currentSessionId;
    };
    

Or if you want to inspect directly, gasss here.

Then why should there be a sessionId?

This is used to 'limit' or limit the number of reactions. I groupBy type where sessionId, then the count is calculated.

If the result has reached the max limit, it means that the user can no longer give a reaction.



REST API

This only needs one endpoint, I made it /reactions so it can only accept the POST method. Accepts the data slug, type, section, and count.

As explained before, all data sent is handled in the Front-End. Except for the sessionId one. ``sessionId'' is generated after the API request.

Well, there is one more endpoint to add, GET /content. This shows the details of the number of slug reactions at the moment, to be used in the component later.

From the previous two tables, you can get this data (response /content):

{
  "meta": {
    "reactionsDetail": {
      "CLAPPING": 65,
      "THINKING": 45,
      "Amazed": 48
    }
  },
  "metaUser": {
    "reactionsDetail": {
      "CLAPPING": 15,
      "THINKING": 15,
      "Amazed": 15
    }
  },
  "metaSection": {
    "features": {
      "reactionsDetail": {
        "CLAPPING": 0,
        "THINKING": 4,
        "Amazed": 0
      }
    },
    "database": {
      "reactionsDetail": {
        "CLAPPING": 0,
        "THINKING": 0,
        "Amazed": 6
      }
    },
  }
}

meta is the total number of reactions from all users, metaUser is the total number of reactions of the current user, and metaSection is the number of reactions on a specific section.

Maybe some people say, it's the `metaSection', why not just make it an array?

To be honest.... it sucks too ๐Ÿ˜‚

But because in the FE logic component I only take data per section (not looping), I prefer to take the data using the `metaSection[section]' method instead of the array which must be filtered first ๐Ÿ‘€


Components

This is the part that I enjoy the most.

First, to retrieve data from the API, I use SWR. Quoted from SWR's own site:

With SWR, components will get a stream of data updates constantly and automatically. And the UI will always be fast and reactive.

It's really good to use this SWR, especially the data mutation.


Now make the reactions component

Component details:

  1. Show reactions that express claps, wow, and hmm.
  2. Display emoji animation on hover.
  3. Batch animation appears after clicking or clicking several times.
  4. Add animation to the reactions counter.
  5. Change the emoji once you hit the limit.

For the emoji animation (point 2) I took it here yes.


For animation matters, still faithfully use framer motion ๐Ÿ˜

When the user clicks on the reaction, the animation appears, when the onClick' event is just a push, the xandy` animation values are random. The animation value was rendered so the component is framer motion.

With the random values of x and y, so when you click a few times, the animation will not be monotonous in that direction.

Are you confused? hehe sorry, try checking full code.


Final Result

This is the final result, try to give reactions, until the limit, there is an animation ๐Ÿ˜

0
0
0
0

Although it still needs a lot of adjustments from styles, pictures, layout or performance, I am happy with the final result at the moment.


Summary

Although making this feature is quite challenging especially in BE, but I quite enjoy all the process. Although it took almost three days cuy ๐Ÿ˜

Recap of tech stacks / libraries used:


WAITTT, is that section reactions? The API already provides a metaSection, what is it used for?

If the data is already there, it is safe, even if the implementation is not yet. But I already have a plan and an idea of โ€‹โ€‹how I want to use it. Just wait for it ๐Ÿ˜

But it will be updated on Twitter, not a special post will be made.

THANK YOU I have read to the end. See you next week in the next post ๐Ÿ‘‹

Posted onfeatureswith tags:
0
0
0
0