Skip to content

Commit

Permalink
Add notification center (#268)
Browse files Browse the repository at this point in the history
* Add notification center

* Fix build

* Apply pr feedback
  • Loading branch information
Julian702 authored Mar 13, 2024
1 parent 5ae6163 commit 493e1c5
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 100 deletions.
62 changes: 62 additions & 0 deletions frontend/components/group/Invitation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Trans } from "@lingui/macro";
import clsx from "clsx";
import Link from "next/link";
import { Check, X } from "react-feather";

import { Button } from "@/components/input/Button";
import { Invitation as InvitationType } from "@/types/Invitation";
import { declineGroupInvitation, sendGroupRequest } from "@/util/api";

import { Text } from "../Text";

interface InvitationProps {
/**
* Invitation
*/
invitation: InvitationType;
/**
* Additional classes
*/
className?: string;
}

/**
* Component for displaying a group invitation in the notification center
*/
export const Invitation = ({ invitation, className }: InvitationProps) => {
return (
<div className={clsx("flex flex-row space-x-3", className)}>
<div className="h-8 w-8 flex-none rounded-full bg-kiokuLightBlue" />
<div className="w-full">
<Text textSize="5xs" className="font-semibold text-black">
<Trans>Group Invitation</Trans>
</Text>
<Text textSize="5xs" className="text-gray-500">
<Trans>
You have been invited to join{" "}
<Link
href={`/group/${invitation.groupID}`}
className="underline hover:text-black"
>
{invitation.groupName}
</Link>
</Trans>
</Text>
</div>
<div className="flex flex-row space-x-1">
<Button
buttonSize="px-3"
buttonStyle="tertiary"
buttonIcon={<X strokeWidth={3}></X>}
onClick={() => declineGroupInvitation(invitation.groupID)}
/>
<Button
buttonSize="px-3 py-1"
buttonStyle="secondary"
buttonIcon={<Check strokeWidth={3}></Check>}
onClick={() => sendGroupRequest(invitation.groupID)}
/>
</div>
</div>
);
};
7 changes: 5 additions & 2 deletions frontend/components/input/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const getStyle = {
secondary:
"bg-black font-medium text-white hover:scale-[1.02] hover:bg-neutral-900",
tertiary:
"bg-transparent font-medium text-kiokuDarkBlue hover:scale-105 hover:bg-gray-100",
"bg-transparent font-medium text-black hover:scale-105 hover:bg-gray-100",
cancel: "bg-transparent font-normal text-gray-400 hover:bg-gray-100",
error: "bg-kiokuRed font-medium text-white hover:scale-105",
warning: "bg-kiokuYellow font-medium text-white hover:scale-105",
Expand Down Expand Up @@ -78,12 +78,15 @@ export const Button = ({
</>
);
const classNames = [
"flex items-center space-x-1 rounded-md outline-none transition",
"flex items-center rounded-md outline-none transition",
className,
];
if (buttonStyle) {
classNames.push(getStyle[buttonStyle]);
}
if (buttonIcon && children) {
classNames.push("space-x-1");
}
classNames.push(buttonSize);

return href ? (
Expand Down
68 changes: 68 additions & 0 deletions frontend/components/modal/NotificationCenter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Trans } from "@lingui/macro";
import { useState } from "react";
import { Bell, Info } from "react-feather";

import { Invitation } from "@/components/group/Invitation";
import { Modal, ModalProps } from "@/components/modal/modal";
import { useInvitations } from "@/util/swr";

/**
* Component for displaying an icon that opens the notification center
*/
export const NotificationCenter = () => {
const { invitations } = useInvitations();

const [showNotificationCenter, setShowNotificationCenter] =
useState<boolean>(false);

return (
<>
<NotificationCenterModal
visible={showNotificationCenter}
setVisible={setShowNotificationCenter}
/>
<div className="relative">
<Bell
className="cursor-pointer"
onClick={() => {
setShowNotificationCenter(true);
}}
/>
{invitations && (
<div className="absolute right-[-0.1rem] top-[-0.15rem] h-3 w-3 flex-none rounded-full bg-kiokuRed">
<div className="absolute h-full w-full animate-[ping_0.8s_ease-out_3] rounded-full bg-kiokuRed" />
</div>
)}
</div>
</>
);
};

/**
* Modal for creating decks
*/
export const NotificationCenterModal = ({
className = "",
setVisible,
...props
}: ModalProps) => {
const { invitations } = useInvitations();

return (
<Modal header="Notification Center" setVisible={setVisible} {...props}>
{invitations ? (
invitations.map((invitation) => (
<Invitation
key={invitation.groupName}
invitation={invitation}
/>
))
) : (
<div className="flex items-center justify-center space-x-3 rounded-md border border-dashed border-gray-500 p-10 text-gray-800">
<Info />
<Trans>You have no pending messages</Trans>
</div>
)}
</Modal>
);
};
2 changes: 1 addition & 1 deletion frontend/components/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const Modal = ({
<div className="flex w-full justify-between">
<Text
textSize="xs"
className="font-bold"
className="font-bold text-black"
>
{header}
</Text>
Expand Down
30 changes: 18 additions & 12 deletions frontend/components/navigation/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { ArrowRight, LogOut } from "react-feather";

import { Logo } from "@/components/graphics/Logo";
import { Button } from "@/components/input/Button";
import { NotificationCenter } from "@/components/modal/NotificationCenter";
import { postRequest } from "@/util/api";
import { logoutRoute } from "@/util/endpoints";
import { authedFetch } from "@/util/reauth";

interface NavbarProps {
/**
Expand All @@ -21,34 +22,39 @@ interface NavbarProps {
*/
export const Navbar = ({ className = "" }: NavbarProps) => {
const router = useRouter();

const [loggedIn, setLoggedIn] = useState<boolean>();

useEffect(() => {
if (router.pathname == "/login") {
setLoggedIn(undefined);
} else {
setLoggedIn(hasCookie("access_token"));
}
}, [router.pathname]);

if (loggedIn == undefined) {
return <></>;
}

return (
<nav
className={`flex items-center justify-between p-5 md:p-10 ${className}`}
>
<Logo href={loggedIn ? "/" : "/home"} />
{loggedIn == true && (
<LogOut
className="cursor-pointer text-kiokuDarkBlue"
onClick={async () => {
const response = await authedFetch(logoutRoute, {
method: "POST",
});
if (response?.ok) {
router.replace("/home");
}
}}
/>
<div className="flex flex-row space-x-8 text-kiokuDarkBlue">
<NotificationCenter />
<LogOut
className="cursor-pointer"
onClick={async () => {
const response = await postRequest(logoutRoute);
if (response?.ok) {
router.replace("/home");
}
}}
/>
</div>
)}
{loggedIn == false && (
<Button
Expand Down
32 changes: 0 additions & 32 deletions frontend/components/navigation/Tabs/InvitationsTabs.tsx

This file was deleted.

13 changes: 0 additions & 13 deletions frontend/components/navigation/Tabs/TabHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ interface TabHeaderProps {
* Style
*/
icon: IconName;
/**
* Text that should be displayed as notification
*/
notificationBadgeContent?: string;
/**
* Additional classes
*/
Expand All @@ -29,7 +25,6 @@ interface TabHeaderProps {
export const TabHeader = ({
name,
icon,
notificationBadgeContent = "",
className = "",
...props
}: TabHeaderProps) => {
Expand All @@ -41,14 +36,6 @@ export const TabHeader = ({
<Icon icon={icon} />

<div>{name}</div>
{notificationBadgeContent && (
<div className="relative flex h-full text-sm text-eggshell">
<div className="absolute inline-flex h-full w-full animate-[ping_1s_ease-out_3] rounded-full bg-kiokuRed opacity-75" />
<div className="relative flex h-full w-full justify-center rounded-full bg-kiokuRed px-2">
{notificationBadgeContent}
</div>
</div>
)}
</div>
);
};
Loading

0 comments on commit 493e1c5

Please sign in to comment.