Skip to content

Commit 762d22c

Browse files
authored
feat: implement like and unlike ability to list item comments control (#1974)
* feat: implement like and unlike ability to list item comments control * refactor code
1 parent 9f91e24 commit 762d22c

File tree

5 files changed

+321
-81
lines changed

5 files changed

+321
-81
lines changed
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export enum ECommentAction {
2-
"ADD" = "ADD",
3-
"DELETE" = "DELETE"
2+
'ADD' = 'ADD',
3+
'DELETE' = 'DELETE',
4+
'LIKE' = 'LIKE',
5+
'UNLIKE' = 'UNLIKE',
46
}

src/controls/listItemComments/components/Comments/CommentsList.tsx

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,22 @@ import { RenderComments } from "./RenderComments";
2020
export const CommentsList: React.FunctionComponent = () => {
2121
const { listItemCommentsState, setlistItemCommentsState } = useContext(ListItemCommentsStateContext);
2222
const { configurationListClasses } = useListItemCommentsStyles();
23-
const { getListItemComments, getNextPageOfComments, addComment, deleteComment } = useSpAPI();
24-
const { comments, isScrolling, pageInfo, commentAction, commentToAdd, selectedComment } = listItemCommentsState;
23+
const {
24+
getListItemComments,
25+
getNextPageOfComments,
26+
addComment,
27+
deleteComment,
28+
likeComment,
29+
unlikeComment,
30+
} = useSpAPI();
31+
const {
32+
comments,
33+
isScrolling,
34+
pageInfo,
35+
commentAction,
36+
commentToAdd,
37+
selectedComment,
38+
} = listItemCommentsState;
2539
const { hasMore, nextLink } = pageInfo;
2640
const scrollPanelRef = useRef<HTMLDivElement>();
2741
const { errorInfo } = listItemCommentsState;
@@ -32,16 +46,23 @@ export const CommentsList: React.FunctionComponent = () => {
3246
type: EListItemCommentsStateTypes.SET_IS_LOADING,
3347
payload: true,
3448
});
35-
const _commentsResults: IlistItemCommentsResults = await getListItemComments();
49+
const _commentsResults: IlistItemCommentsResults =
50+
await getListItemComments();
3651
setlistItemCommentsState({
3752
type: EListItemCommentsStateTypes.SET_LIST_ITEM_COMMENTS,
3853
payload: _commentsResults.comments,
3954
});
4055
setlistItemCommentsState({
4156
type: EListItemCommentsStateTypes.SET_DATA_PAGE_INFO,
42-
payload: { hasMore: _commentsResults.hasMore, nextLink: _commentsResults.nextLink } as IPageInfo,
57+
payload: {
58+
hasMore: _commentsResults.hasMore,
59+
nextLink: _commentsResults.nextLink,
60+
} as IPageInfo,
61+
});
62+
setlistItemCommentsState({
63+
type: EListItemCommentsStateTypes.SET_COMMENT_ACTION,
64+
payload: undefined,
4365
});
44-
setlistItemCommentsState({ type: EListItemCommentsStateTypes.SET_COMMENT_ACTION, payload: undefined });
4566
setlistItemCommentsState({
4667
type: EListItemCommentsStateTypes.SET_IS_LOADING,
4768
payload: false,
@@ -99,25 +120,110 @@ export const CommentsList: React.FunctionComponent = () => {
99120
[setlistItemCommentsState, _loadComments]
100121
);
101122

123+
const _onCommentLike = useCallback(
124+
async (commentId: number) => {
125+
try {
126+
const _errorInfo: IErrorInfo = { showError: false, error: undefined };
127+
setlistItemCommentsState({
128+
type: EListItemCommentsStateTypes.SET_ERROR_INFO,
129+
payload: _errorInfo,
130+
});
131+
await likeComment(commentId);
132+
await _loadComments();
133+
} catch (error) {
134+
const _errorInfo: IErrorInfo = { showError: true, error: error };
135+
setlistItemCommentsState({
136+
type: EListItemCommentsStateTypes.SET_ERROR_INFO,
137+
payload: _errorInfo,
138+
});
139+
}
140+
},
141+
[setlistItemCommentsState, _loadComments]
142+
);
143+
const _onCommentUnlike = useCallback(
144+
async (commentId: number) => {
145+
try {
146+
const _errorInfo: IErrorInfo = { showError: false, error: undefined };
147+
setlistItemCommentsState({
148+
type: EListItemCommentsStateTypes.SET_ERROR_INFO,
149+
payload: _errorInfo,
150+
});
151+
await unlikeComment(commentId);
152+
await _loadComments();
153+
} catch (error) {
154+
const _errorInfo: IErrorInfo = { showError: true, error: error };
155+
setlistItemCommentsState({
156+
type: EListItemCommentsStateTypes.SET_ERROR_INFO,
157+
payload: _errorInfo,
158+
});
159+
}
160+
},
161+
[setlistItemCommentsState, _loadComments]
162+
);
163+
102164
useEffect(() => {
103165
switch (commentAction) {
104166
case ECommentAction.ADD:
105167
(async () => {
106168
// Add new comment
107169
await _onAddComment(commentToAdd);
108-
})().then(() => { /* no-op; */}).catch(() => { /* no-op; */ });
170+
})()
171+
.then(() => {
172+
/* no-op; */
173+
})
174+
.catch(() => {
175+
/* no-op; */
176+
});
177+
break;
178+
case ECommentAction.LIKE:
179+
(async () => {
180+
// Add new comment
181+
const commentId = Number(selectedComment.id);
182+
await _onCommentLike(commentId);
183+
})()
184+
.then(() => {
185+
/* no-op; */
186+
})
187+
.catch(() => {
188+
/* no-op; */
189+
});
190+
break;
191+
case ECommentAction.UNLIKE:
192+
(async () => {
193+
// Add new comment
194+
const commentId = Number(selectedComment.id);
195+
await _onCommentUnlike(commentId);
196+
})()
197+
.then(() => {
198+
/* no-op; */
199+
})
200+
.catch(() => {
201+
/* no-op; */
202+
});
109203
break;
110204
case ECommentAction.DELETE:
111205
(async () => {
112206
// delete comment
113207
const commentId = Number(selectedComment.id);
114208
await _onADeleteComment(commentId);
115-
})().then(() => { /* no-op; */}).catch(() => { /* no-op; */ });
209+
})()
210+
.then(() => {
211+
/* no-op; */
212+
})
213+
.catch(() => {
214+
/* no-op; */
215+
});
116216
break;
117217
default:
118218
break;
119219
}
120-
}, [commentAction, selectedComment, commentToAdd, _onAddComment, _onADeleteComment]);
220+
}, [
221+
commentAction,
222+
selectedComment,
223+
commentToAdd,
224+
_onAddComment,
225+
_onADeleteComment,
226+
]);
121227

122228
useEffect(() => {
123229
(async () => {

src/controls/listItemComments/components/Comments/RenderComments.tsx

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,96 @@
1-
import { IconButton } from "@fluentui/react/lib/Button";
2-
import { DocumentCard, DocumentCardDetails } from "@fluentui/react/lib/DocumentCard";
3-
import { Stack } from "@fluentui/react/lib/Stack";
4-
import * as React from "react";
5-
import { useCallback } from "react";
6-
import { useContext } from "react";
7-
import { ConfirmDelete } from "../ConfirmDelete/ConfirmDelete";
8-
import { EListItemCommentsStateTypes, ListItemCommentsStateContext } from "../ListItemCommentsStateProvider";
9-
import { CommentItem } from "./CommentItem";
10-
import { IComment } from "./IComment";
11-
import { RenderSpinner } from "./RenderSpinner";
12-
import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
13-
import { useBoolean } from "@fluentui/react-hooks";
14-
import { List } from "@fluentui/react/lib/List";
15-
import { AppContext, ECommentAction } from "../..";
1+
import { IconButton } from '@fluentui/react/lib/Button';
2+
import {
3+
DocumentCard,
4+
DocumentCardDetails,
5+
} from '@fluentui/react/lib/DocumentCard';
6+
import { Stack } from '@fluentui/react/lib/Stack';
7+
import * as React from 'react';
8+
import { useCallback } from 'react';
9+
import { useContext } from 'react';
10+
import { ConfirmDelete } from '../ConfirmDelete/ConfirmDelete';
11+
import {
12+
EListItemCommentsStateTypes,
13+
ListItemCommentsStateContext,
14+
} from '../ListItemCommentsStateProvider';
15+
import { CommentItem } from './CommentItem';
16+
import { IComment } from './IComment';
17+
import { RenderSpinner } from './RenderSpinner';
18+
import { useListItemCommentsStyles } from './useListItemCommentsStyles';
19+
import { useBoolean } from '@fluentui/react-hooks';
20+
import { List, Text } from '@fluentui/react';
21+
import { AppContext, ECommentAction } from '../..';
1622

17-
export interface IRenderCommentsProps { }
23+
export interface IRenderCommentsProps {}
1824

19-
export const RenderComments: React.FunctionComponent<IRenderCommentsProps> = () => {
25+
export const RenderComments: React.FunctionComponent<
26+
IRenderCommentsProps
27+
> = () => {
2028
const { highlightedCommentId } = useContext(AppContext);
21-
const { listItemCommentsState, setlistItemCommentsState } = useContext(ListItemCommentsStateContext);
22-
const { documentCardStyles,documentCardHighlightedStyles, itemContainerStyles, deleteButtonContainerStyles } = useListItemCommentsStyles();
29+
const { listItemCommentsState, setlistItemCommentsState } = useContext(
30+
ListItemCommentsStateContext
31+
);
32+
const {
33+
documentCardStyles,
34+
documentCardHighlightedStyles,
35+
itemContainerStyles,
36+
buttonsContainerStyles,
37+
} = useListItemCommentsStyles();
2338
const { comments, isLoading } = listItemCommentsState;
2439

2540
const [hideDialog, { toggle: setHideDialog }] = useBoolean(true);
2641

42+
const _likeComment = useCallback(() => {
43+
setlistItemCommentsState({
44+
type: EListItemCommentsStateTypes.SET_COMMENT_ACTION,
45+
payload: ECommentAction.LIKE,
46+
});
47+
}, []);
48+
49+
const _unLikeComment = useCallback(() => {
50+
setlistItemCommentsState({
51+
type: EListItemCommentsStateTypes.SET_COMMENT_ACTION,
52+
payload: ECommentAction.UNLIKE,
53+
});
54+
}, []);
55+
2756
const onRenderCell = useCallback(
2857
(comment: IComment, index: number): JSX.Element => {
2958
return (
30-
<DocumentCard styles={ highlightedCommentId && comment.id===highlightedCommentId? documentCardHighlightedStyles : documentCardStyles} key={index}>
31-
<Stack horizontal horizontalAlign="end" styles={deleteButtonContainerStyles}>
59+
<DocumentCard
60+
styles={
61+
highlightedCommentId && comment.id === highlightedCommentId
62+
? documentCardHighlightedStyles
63+
: documentCardStyles
64+
}
65+
key={index}
66+
>
67+
<Stack
68+
horizontal
69+
horizontalAlign="end"
70+
styles={buttonsContainerStyles}
71+
>
72+
<div style={{ display: 'flex', alignItems: 'center' }}>
73+
<Text>{comment.likeCount}</Text>
74+
<IconButton
75+
iconProps={{
76+
iconName: `${comment.isLikedByUser ? 'LikeSolid' : 'Like'}`,
77+
}}
78+
style={{ fontSize: 10 }}
79+
onClick={() => {
80+
setlistItemCommentsState({
81+
type: EListItemCommentsStateTypes.SET_SELECTED_COMMENT,
82+
payload: comment,
83+
});
84+
if (!comment.isLikedByUser) {
85+
_likeComment();
86+
} else {
87+
_unLikeComment();
88+
}
89+
}}
90+
/>
91+
</div>
3292
<IconButton
33-
iconProps={{ iconName: "Delete" }}
93+
iconProps={{ iconName: 'Delete' }}
3494
style={{ fontSize: 10 }}
3595
onClick={async () => {
3696
setlistItemCommentsState({
@@ -57,10 +117,13 @@ export const RenderComments: React.FunctionComponent<IRenderCommentsProps> = ()
57117
},
58118
[comments]
59119
);
60-
61120
return (
62121
<>
63-
{isLoading ? <RenderSpinner /> : <List items={comments} onRenderCell={onRenderCell} />}
122+
{isLoading ? (
123+
<RenderSpinner />
124+
) : (
125+
<List items={comments} onRenderCell={onRenderCell} />
126+
)}
64127
<ConfirmDelete
65128
hideDialog={hideDialog}
66129
onDismiss={(deleteComment: boolean) => {

0 commit comments

Comments
 (0)