Skip to content

Instantly share code, notes, and snippets.

@steveyerigandev
Last active September 2, 2021 15:21
Show Gist options
  • Save steveyerigandev/d14e9d8bd28e81c13d31612e0e7f5abe to your computer and use it in GitHub Desktop.
Save steveyerigandev/d14e9d8bd28e81c13d31612e0e7f5abe to your computer and use it in GitHub Desktop.
React Functional Component - NotificationsDropdown (using styled components)
import React, { useEffect } from "react";
import styled from "styled-components";
import NavDropdown from "react-bootstrap/NavDropdown";
import {
listNotifications,
partialUpdateNotifications,
} from "features/notifications/thunks";
import bellIcon from "images/bell.svg";
import { useDispatch, useSelector } from "react-redux";
import { useToasts } from "react-toast-notifications";
import { useNavigate } from "@reach/router";
import { setDisplayCommentFor } from "features/notifications/slice";
import { getUserImage } from "utils/users";
import Moment from "react-moment";
import FireIcon from "images/fire.svg";
const StyledNavDropdown = styled(NavDropdown)`
.dropdown-toggle::before {
content: ${(props) =>
props.totalnots ? '"' + props.totalnots + '"' : null};
display: inline-block;
width: 18px;
height: 18px;
font-size: 12px;
text-align: center;
-webkit-border-radius: 7.5px;
border-radius: 7.5px;
background-color: ${(props) => props.theme.black};
position: absolute;
top: 5px;
right: 5px;
color: ${(props) => props.theme.yellow};
}
a::after {
content: none;
}
.dropdown-menu {
overflow-y: auto;
padding: 20px 10px;
width: 400px;
margin: 0px;
position: fixed !important;
top: 0px;
bottom: 0px;
max-height: 100%;
background-color: ${(props) => props.theme.lime};
&::-webkit-scrollbar {
width: 0.5em;
height: 0.5em;
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
background-color: white;
}
&::-webkit-scrollbar-thumb {
background-color: #777777;
border-radius: 3px;
&:hover {
background: #777777;
}
}
@media (max-width: ${(props) => props.theme.smBreakpoint}) {
width: 320px;
}
}
@media (max-width: ${(props) => props.theme.tabletBreakpoint}) {
position: fixed;
top: 4px;
right: 32px;
}
`;
const StyledDropDownItem = styled(NavDropdown.Item)`
display: flex;
margin-bottom: 2px;
margin-top: 2px;
padding: 15px 30px 15px 15px;
border-radius: 5px;
position: relative;
white-space: normal;
font-size: 14px;
align-items: center;
border-bottom: ${(props) =>
props.endunseen ? "2px dashed rgba(124, 103, 93, 0.3)" : "none"};
&:hover,
&:active,
&:focus {
background-color: transparent !important;
color: black !important;
}
&.active {
color: black;
}
`;
const StyledNotLink = styled.div`
border-radius: 50%;
padding-right: 6px !important;
padding-left: 6px !important;
background-color: ${(props) => props.theme.neonPink};
padding: 3px 6px 6px 6px;
&:hover {
background-color: ${(props) => props.theme.black};
}
@media (max-width: ${(props) => props.theme.smBreakpoint}) {
.show & {
z-index: 10000;
position: fixed;
right: 282px;
top: 22px;
}
}
`;
const NotificationHeader = styled.h3`
font-family: SuisseIntl;
font-style: normal;
font-weight: 900;
font-size: 34px;
line-height: 34px;
letter-spacing: 0.06em;
text-align: center;
margin-bottom: 1rem;
color: ${(props) => props.theme.neonPink};
transform: matrix(1, -0.03, 0.04, 1, 0, 0);
@media (max-width: ${(props) => props.theme.smBreakpoint}) {
font-size: 26px;
}
`;
const LikeFireIcon = styled.img`
position: absolute;
width: 25px;
bottom: -7px;
right: -3px;
`;
const ImageWrapper = styled.div`
width: 45px;
border-radius: 50%;
margin-right: 10px;
position: relative;
`;
const StyledProfileIcon = styled.img`
width: 45px;
border-radius: 50%;
box-shadow: 2px 2px 1px ${(props) => props.theme.gray300};
`;
const StyledNotIcon = styled.img`
width: 20px;
`;
const NotContent = styled.div`
opacity: ${(props) => (!props.seen ? "1" : ".5")};
color: ${(props) => props.theme.black};
font-size: 15px;
line-height: 21px;
`;
const StyledMoment = styled(Moment)`
color: #7c675d;
`;
function NotificationsDropdown() {
// Provides the a dropdown for the list of notifcations related to the logged in user.
const notifcationsState = useSelector((state) => state.notifications);
const { isLoading, entities } = notifcationsState;
const { addToast } = useToasts();
const dispatch = useDispatch();
const navigate = useNavigate();
const triggerFetchNotifications = async () => {
try {
const action = await dispatch(listNotifications({ params: {} }));
if (action.type === "LIST_NOTIFICATIONS/rejected") {
addToast("Error occured while fetching notifications", {
appearance: "error",
autoDismiss: true,
});
}
} catch (err) {
addToast("Error occured while fetching notifications", {
appearance: "error",
autoDismiss: true,
});
}
};
const renderNotificationContent = (notification) => {
const { target, verb, actor } = notification.action;
const actorUsername = actor ? actor.username : "";
let content = "";
switch (notification.action.verb) {
case "made a comment":
content = `<strong> ${actorUsername} </strong> ${verb} on ${
target.bucket
? target.bucket.title
: target.title
? target.title
: "your post"
}`;
return { __html: content };
case "mentioned":
content = `<strong> ${actorUsername} </strong> has ${verb} you`;
return { __html: content };
case "liked":
content = `<strong> ${actorUsername} </strong> has Liked your post ${
target.text ? target.text : ""
}.`;
return { __html: content };
case "replied to a comment":
content = `<strong> ${actorUsername} </strong> replied to your comment.`;
return { __html: content };
default:
content = "";
return { __html: content };
}
};
const markNotificationAsSeen = async (notification) => {
try {
const action = await dispatch(
partialUpdateNotifications({
objectId: notification.id,
data: { seen: true },
})
);
if (action.type === "PATCH_NOTIFICATIONS/rejected") {
addToast("Error occured while marking notification seen", {
appearance: "error",
autoDismiss: true,
});
}
} catch (e) {
addToast("Error occured while marking notification seen", {
appearance: "error",
autoDismiss: true,
});
}
};
const redirectToNotificationUrl = (notification) => {
const { target, actionObject } = notification.action;
var url = "/feed";
switch (notification.action.verb) {
case "made a comment":
url = `/feed?object_id=${actionObject.objectId}&content_type=${actionObject.contentType}`;
break;
case "mentioned":
url = `/feed?object_id=${target.objectId}&content_type=${target.contentType}`;
break;
case "liked":
url = `/feed?object_id=${target.objectId}&content_type=${target.contentType}`;
break;
case "replied to a comment":
url = `/feed?object_id=${target.objectId}&content_type=${target.contentType}`;
dispatch(setDisplayCommentFor({ commentId: target.id }));
break;
default:
url = "/feed";
}
navigate(url);
};
useEffect(() => {
triggerFetchNotifications();
setInterval(triggerFetchNotifications, 30000);
}, []);
const notificationsCount = entities
? entities.results
? entities.results.filter((entity) => !entity.seen).length
: 0
: 0;
return (
<StyledNavDropdown
totalnots={notificationsCount}
title={
<StyledNotLink>
<StyledNotIcon src={bellIcon} alt="Rad how to school notification" />
</StyledNotLink>
}
id="notifications-navbar-dropdown"
alignRight
>
<>
<NotificationHeader> notifications </NotificationHeader>
{isLoading
? "Loading"
: entities.results && entities.results.length
? entities.results.map((notification, index) => {
const isEndUnSeen =
index < entities.results.length - 1 &&
!entities.results[index].seen &
entities.results[index + 1].seen;
return (
<StyledDropDownItem
seen={notification.seen ? "seen" : null}
key={notification.id}
endunseen={isEndUnSeen ? "end" : null}
onClick={() => {
markNotificationAsSeen(notification);
redirectToNotificationUrl(notification);
}}
>
<ImageWrapper>
{notification.action.verb === "liked" ? (
<LikeFireIcon src={FireIcon} />
) : (
""
)}
<StyledProfileIcon
src={getUserImage(notification.action.actor)}
/>
</ImageWrapper>
<div>
<NotContent
dangerouslySetInnerHTML={renderNotificationContent(
notification
)}
seen={notification.seen ? "seen" : null}
/>
<StyledMoment fromNow>
{notification.action.timestamp}
</StyledMoment>
</div>
</StyledDropDownItem>
);
})
: ""}
</>
</StyledNavDropdown>
);
}
export default NotificationsDropdown;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment