Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification updates #5239

Merged
merged 9 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion app/client/src/actions/notificationActions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { AppsmithNotification } from "entities/Notification";

export const fetchNotificationsRequest = () => ({
export const fetchNotificationsRequest = (beforeTime?: string) => ({
type: ReduxActionTypes.FETCH_NOTIFICATIONS_REQUEST,
payload: beforeTime,
});

export const fetchNotificationsSuccess = (payload: {
Expand All @@ -29,3 +30,29 @@ export const markAllNotificationsAsReadRequest = () => ({
export const markAllNotificationsAsReadSuccess = () => ({
type: ReduxActionTypes.MARK_ALL_NOTIFICATIONS_AS_READ_SUCCESS,
});

export const resetNotifications = (payload: {
notifications: Array<AppsmithNotification>;
}) => ({
type: ReduxActionTypes.RESET_NOTIFICATIONS,
payload,
});

export const fetchUnreadNotificationsCountRequest = () => ({
type: ReduxActionTypes.FETCH_UNREAD_NOTIFICATIONS_COUNT_REQUEST,
});

export const fetchUnreadNotificationsCountSuccess = (payload: number) => ({
type: ReduxActionTypes.FETCH_UNREAD_NOTIFICATIONS_COUNT_SUCCESS,
payload,
});

export const markNotificationAsReadRequest = (payload: string) => ({
type: ReduxActionTypes.MARK_NOTIFICATION_AS_READ_REQUEST,
payload,
});

export const markNotificationAsReadSuccess = (payload: string) => ({
type: ReduxActionTypes.MARK_NOTIFICATION_AS_READ_SUCCESS,
payload,
});
27 changes: 21 additions & 6 deletions app/client/src/api/NotificationsAPI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,32 @@ import { AxiosPromise } from "axios";
import Api from "./Api";
import { ApiResponse } from "./ApiResponses";

class NotificaitonsApi extends Api {
class NotificationsApi extends Api {
static baseURL = "v1/notifications";
static markAsReadURL = `${NotificationsApi.baseURL}/isRead`;
static markAllAsReadURL = `${NotificationsApi.markAsReadURL}/all`;
static fetchUnreadNotificationsCountURL = `${NotificationsApi.baseURL}/count/unread`;

static fetchNotifications(): AxiosPromise<ApiResponse> {
return Api.get(NotificaitonsApi.baseURL);
static fetchNotifications(beforeDate?: string): AxiosPromise<ApiResponse> {
return Api.get(NotificationsApi.baseURL, beforeDate ? { beforeDate } : {});
}

// TODO update mark all as read notifications api
static markAllNotificationsAsRead(): AxiosPromise<ApiResponse> {
return Api.get(NotificaitonsApi.baseURL);
return Api.patch(NotificationsApi.markAllAsReadURL, { isRead: true });
}

static fetchUnreadNotificationsCount(): AxiosPromise<ApiResponse> {
return Api.get(NotificationsApi.fetchUnreadNotificationsCountURL);
}

static markNotificationsAsRead(
ids: Array<string>,
): AxiosPromise<ApiResponse> {
return Api.patch(NotificationsApi.markAsReadURL, {
isRead: true,
idList: ids,
});
}
}

export default NotificaitonsApi;
export default NotificationsApi;
32 changes: 32 additions & 0 deletions app/client/src/assets/icons/comments/notifications-empty-state.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/client/src/constants/DefaultTheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ const notifications = {
markAllAsReadButtonText: "#716E6E",
unreadIndicator: "#F86A2B",
bellIndicator: "#E22C2C",
label: "#858282",
};

const displayImageUpload = {
Expand Down
23 changes: 23 additions & 0 deletions app/client/src/constants/ReduxActionConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export const ReduxSagaChannels: { [key: string]: string } = {
};

export const ReduxActionTypes: { [key: string]: string } = {
MARK_NOTIFICATION_AS_READ_REQUEST: "MARK_NOTIFICATION_AS_READ_REQUEST",
MARK_NOTIFICATION_AS_READ_SUCCESS: "MARK_NOTIFICATION_AS_READ_SUCCESS",
FETCH_UNREAD_NOTIFICATIONS_COUNT_REQUEST:
"FETCH_UNREAD_NOTIFICATIONS_COUNT_REQUEST",
FETCH_UNREAD_NOTIFICATIONS_COUNT_SUCCESS:
"FETCH_UNREAD_NOTIFICATIONS_COUNT_SUCCESS",
RESET_NOTIFICATIONS: "RESET_NOTIFICATIONS",
MARK_ALL_NOTIFICATIONS_AS_READ_REQUEST:
"MARK_ALL_NOTIFICATIONS_AS_READ_REQUEST",
MARK_ALL_NOTIFICATIONS_AS_READ_SUCCESS:
Expand Down Expand Up @@ -455,6 +462,22 @@ export const ReduxActionTypes: { [key: string]: string } = {
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];

export const ReduxActionErrorTypes: { [key: string]: string } = {
FETCH_NOTIFICATIONS_ERROR: "FETCH_NOTIFICATIONS_ERROR",
MARK_ALL_NOTIFICAIONS_AS_READ_ERROR: "MARK_ALL_NOTIFICAIONS_AS_READ_ERROR",
FETCH_UNREAD_NOTIFICATIONS_COUNT_ERROR:
"FETCH_UNREAD_NOTIFICATIONS_COUNT_ERROR",
MARK_NOTIFICATION_AS_READ_ERROR: "MARK_NOTIFICATION_AS_READ_ERROR",
CREATE_COMMENT_THREAD_ERROR: "CREATE_COMMENT_THREAD_ERROR",
ADD_COMMENT_TO_THREAD_ERROR: "ADD_COMMENT_TO_THREAD_ERROR",
FETCH_APPLICATION_COMMENTS_ERROR: "FETCH_APPLICATION_COMMENTS_ERROR",
SET_COMMENT_RESOLUTION_ERROR: "SET_COMMENT_RESOLUTION_ERROR",
PIN_COMMENT_THREAD_ERROR: "PIN_COMMENT_THREAD_ERROR",
DELETE_COMMENT_ERROR: "DELETE_COMMENT_ERROR",
MARK_THREAD_AS_READ_ERROR: "MARK_THREAD_AS_READ_ERROR",
EDIT_COMMENT_ERROR: "EDIT_COMMENT_ERROR",
DELETE_COMMENT_THREAD_ERROR: "DELETE_COMMENT_THREAD_ERROR",
ADD_COMMENT_REACTION_ERROR: "ADD_COMMENT_REACTION_ERROR",
DELETE_COMMENT_REACTION_ERROR: "DELETE_COMMENT_REACTION_ERROR",
INITIALIZE_APPSMITH_ERROR: "INITIALIZE_APPSMITH_ERROR",
API_ERROR: "API_ERROR",
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
Expand Down
1 change: 1 addition & 0 deletions app/client/src/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ export const FIRST_AND_LAST_NAME = () => "First and last name";
export const MARK_ALL_AS_READ = () => "Mark all as read";
export const INVITE_A_NEW_USER = () => "Invite a new user";
export const REMOVE = () => "Remove";
export const NO_NOTIFICATIONS_TO_SHOW = () => "No notifications to show";

// Showcase Carousel
export const NEXT = () => "NEXT";
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/notifications/Bell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useDispatch } from "react-redux";

import {
fetchNotificationsRequest,
fetchUnreadNotificationsCountRequest,
setIsNotificationsListVisible,
} from "actions/notificationActions";
import styled from "styled-components";
Expand Down Expand Up @@ -58,6 +59,7 @@ function Bell() {

useEffect(() => {
dispatch(fetchNotificationsRequest());
dispatch(fetchUnreadNotificationsCountRequest());
}, []);

const unreadCount = useSelector(unreadCountSelector);
Expand Down
132 changes: 100 additions & 32 deletions app/client/src/notifications/NotificationListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import React from "react";
import ProfileImage, { Profile } from "pages/common/ProfileImage";

import styled from "styled-components";

import UserApi from "api/UserApi";

import { AppsmithNotification, NotificationTypes } from "entities/Notification";

import { getTypographyByKey } from "constants/DefaultTheme";
// import moment from "moment";

import { getCommentThreadURL } from "comments/utils";
import { markNotificationAsReadRequest } from "actions/notificationActions";

import history from "utils/history";
import { useDispatch } from "react-redux";
import { markThreadAsReadRequest } from "actions/commentActions";

import moment from "moment";
import styled from "styled-components";

const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -59,22 +57,61 @@ const UnreadIndicator = styled.div`
props.theme.colors.notifications.unreadIndicator};
`;

// eslint-disable-next-line
function CommentNotification(props: { notification?: AppsmithNotification }) {
// TODO handle click
function CommentNotification(props: { notification: AppsmithNotification }) {
const dispatch = useDispatch();
const {
_id,
comment,
createdAt,
creationTime,
id,
isRead,
} = props.notification;
const {
applicationId,
applicationName,
authorName,
authorUsername,
// mode, TODO get from comment thread
pageId,
// resolvedState, TODO get from comment thread
threadId,
} = comment;

const commentThreadUrl = getCommentThreadURL({
applicationId,
commentThreadId: threadId,
// isResolved: resolvedState?.active,
// mode,
pageId,
});

const _createdAt = createdAt || creationTime;
const displayName = authorName || authorUsername;

const handleClick = () => {
history.push(
`${commentThreadUrl.pathname}${commentThreadUrl.search}${commentThreadUrl.hash}`,
);

dispatch(markNotificationAsReadRequest(id || (_id as string)));
};

return (
<FlexContainer>
<ProfileImage
side={25}
source={`/api/${UserApi.photoURL}`}
userName={""}
/>
<FlexContainer onClick={handleClick}>
<ProfileImageContainer>
<ProfileImage
side={25}
source={`/api/${UserApi.photoURL}/${authorUsername}`}
userName={displayName}
/>
{!isRead && <UnreadIndicator />}
</ProfileImageContainer>
<NotificationBodyContainer>
<div>
<b>Comment</b> notification body
<b>{displayName}</b> left a comment on <b>{applicationName}</b>
</div>
{/* <Time>{moment().fromNow()}</Time> */}
<Time>An hour ago</Time>
<Time>{moment(_createdAt).fromNow()}</Time>
</NotificationBodyContainer>
</FlexContainer>
);
Expand All @@ -84,40 +121,67 @@ function CommentThreadNotification(props: {
notification: AppsmithNotification;
}) {
const dispatch = useDispatch();
const { commentThread } = props.notification;
// TODO add isResolved, applicationId, pageId
// mode: commentThread?.mode
const {
_id: _notificationId,
commentThread,
createdAt,
creationTime,
id: notificationId,
isRead,
} = props.notification;

const {
_id,
applicationId,
applicationName,
authorName,
authorUsername,
id,
mode,
pageId,
resolvedState,
} = commentThread;

const commentThreadId = _id || id;

const commentThreadUrl = getCommentThreadURL({
commentThreadId: commentThread?.id,
applicationId,
commentThreadId,
isResolved: resolvedState?.active,
mode,
pageId,
});

const handleClick = () => {
history.push(
`${commentThreadUrl.pathname}${commentThreadUrl.search}${commentThreadUrl.hash}`,
);

dispatch(markThreadAsReadRequest(commentThread?.id));
dispatch(
markNotificationAsReadRequest(
notificationId || (_notificationId as string),
),
);
};

// TODO use notification isRead state
const isRead = true;
const _createdAt = createdAt || creationTime;
const displayName = authorName || authorUsername;

return (
<FlexContainer onClick={handleClick}>
<ProfileImageContainer>
<ProfileImage
side={25}
source={`/api/${UserApi.photoURL}`}
userName={""}
source={`/api/${UserApi.photoURL}/${authorUsername}`}
userName={displayName}
/>
{!isRead && <UnreadIndicator />}
</ProfileImageContainer>
<NotificationBodyContainer>
<div>
<b>Comment Thread</b> notification body
<b>{displayName}</b> left a comment on <b>{applicationName}</b>
</div>
{/* <Time>{moment().fromNow()}</Time> */}
<Time>An hour ago</Time>
<Time>{moment(_createdAt).fromNow()}</Time>
</NotificationBodyContainer>
</FlexContainer>
);
Expand All @@ -129,8 +193,12 @@ const notificationByType = {
};

function NotificationListItem(props: { notification: AppsmithNotification }) {
const Component =
notificationByType[NotificationTypes.CommentThreadNotification];
// TODO fix this, use type from api response
const type = props.notification.comment
? NotificationTypes.CommentNotification
: NotificationTypes.CommentThreadNotification;

const Component = notificationByType[type];

return (
<Container>
Expand Down
Loading