Role-based API, system notifications & intercepted routes improvements

leojuriolli profile picture.

leojuriolli 11 months ago

Yesterday I added roles to every user. The default role is user, and the other one is admin. To make it easier to manage on the codebase, I used Next Auth callbacks to add a boolean to the user session object called isAdmin:

 callbacks: {
    session: async ({ session, user }) => {
      session.user.id = user.id;

      // new:
      session.user.isAdmin = user.role === "admin";

      return Promise.resolve(session);
    },
  },

New notifications

I also added system notifications to send alerts to users. There are four types of system notifications, and all of them are sent only when a new user signs in for the first time:

  1. Welcome notification.
  2. Call to action to create your first post.
  3. Suggest adding a profile picture if user does not have one.
  4. Suggest adding a username if empty.

For developing this I used Next Auth's newUser page. This page is where Next Auth redirects a user when they sign in for the first time.

There, all we do is create the notifications, it shouldn't even be noticiable that there was a change of pages, before being redirected back to the default flow:

export async function getServerSideProps(context: GetServerSidePropsContext) {
  const session = await getServerSession(context.req, context.res, authOptions);

  const callbackUrl = context.query.callbackUrl as string;

  if (!session?.user) {
    return { redirect: { destination: "/" } };
  }

  // Only create welcome notifications once.
  const userHasAlreadyBeenWelcomed = await prisma.notification.findFirst({
    where: {
      type: "WELCOME",
      notifiedId: session?.user.id,
    },
  });

  if (!userHasAlreadyBeenWelcomed) {
    const welcomeNotification = prisma.notification.create({
      data: {
        type: "WELCOME" as const,
        notifierId: session?.user.id,
        notifiedId: session?.user.id,
      },
    });

    const firstPostNotification = prisma.notification.create({
      data: {
        type: "FIRST-POST" as const,
        notifierId: session?.user.id,
        notifiedId: session?.user.id,
      },
    });

    // fetching in parallel to reduce wait.
    await Promise.all([welcomeNotification, firstPostNotification]);
  }

  return { redirect: { destination: callbackUrl || "/" } };
}

Intercepted routes improvements

Intercepted routes for posts were working, but when trying to navigate with the browser back/forward buttons, it wouldn't open the post. To fix this, I changed the approach:

Before, there was a <PostModal> inside every card, and it would only open onClick to the <Link>. I changed it so there is only one <PostModal /> on the whole app, and it tracks when to be opened or closed based on the router state (query, current route, etc.)

Other changes include:

  • Bugfixes
  • All dates are formatted on the server-side now.
  • Navigation sidebar links isActive state was being set to false after opening a post modal.

Tags

Comments

Loading...
Loading...
Loading...