From 853430a9f651d3a225177b558fdb5d44484b81c8 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Wed, 26 Jul 2023 16:23:54 +0530 Subject: [PATCH 1/6] feat: developed reaction selector component --- apps/app/components/core/index.ts | 1 + .../app/components/core/reaction-selector.tsx | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 apps/app/components/core/reaction-selector.tsx diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index b91944abfa9..5ffb8d093ca 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -10,3 +10,4 @@ export * from "./sidebar"; export * from "./issues-view"; export * from "./image-picker-popover"; export * from "./feeds"; +export * from "./reaction-selector"; diff --git a/apps/app/components/core/reaction-selector.tsx b/apps/app/components/core/reaction-selector.tsx new file mode 100644 index 00000000000..1ad29452fb5 --- /dev/null +++ b/apps/app/components/core/reaction-selector.tsx @@ -0,0 +1,99 @@ +import { Fragment } from "react"; + +import { Popover, Transition } from "@headlessui/react"; + +import { renderEmoji } from "helpers/emoji.helper"; + +import { Icon } from "components/ui"; + +const emojis = ["128077", "128078", "128516", "128165", "128533", "129505", "9992", "128064"]; + +interface CommonProps { + position?: "top" | "bottom"; + size?: "sm" | "md" | "lg"; +} + +interface SingleEmojiProps extends CommonProps { + multiple?: false; + value?: string | null; + onSelect: (emoji: string) => void; +} + +interface MultipleEmojiProps extends CommonProps { + multiple: true; + value?: string[] | null; + onSelect: (emoji: string[]) => void; +} + +type Props = SingleEmojiProps | MultipleEmojiProps; + +export const ReactionSelector: React.FC = (props) => { + const { value, onSelect, multiple, position, size } = props; + + return ( + + {({ open, close: closePopover }) => ( + <> + + + {value ? ( + + {renderEmoji(Array.isArray(value) ? value[value.length - 1] : value)} + + ) : ( + + )} + + + + +
+
+ {emojis.map((emoji) => ( + + ))} +
+
+
+
+ + )} +
+ ); +}; From acab63a0a69246f5fdf7916a42754273b1a57a00 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 27 Jul 2023 16:58:18 +0530 Subject: [PATCH 2/6] feat add reaction on issue & issue comment refactor: reaction selector component, made hooks to abstracted reaction logic & state by making custom hook --- .../app/components/core/reaction-selector.tsx | 64 +++----- .../issues/comment/comment-card.tsx | 3 + .../issues/comment/comment-reaction.tsx | 82 ++++++++++ apps/app/components/issues/comment/index.ts | 1 + apps/app/components/issues/index.ts | 1 + apps/app/components/issues/issue-reaction.tsx | 66 ++++++++ apps/app/components/issues/main-content.tsx | 4 + apps/app/constants/fetch-keys.ts | 10 ++ apps/app/helpers/emoji.helper.ts | 15 ++ apps/app/hooks/use-comment-reaction.tsx | 95 ++++++++++++ apps/app/hooks/use-issue-reaction.tsx | 96 ++++++++++++ apps/app/services/reaction.service.ts | 145 ++++++++++++++++++ apps/app/services/track-event.service.ts | 34 ++++ apps/app/types/index.d.ts | 1 + apps/app/types/reaction.d.ts | 33 ++++ 15 files changed, 611 insertions(+), 39 deletions(-) create mode 100644 apps/app/components/issues/comment/comment-reaction.tsx create mode 100644 apps/app/components/issues/issue-reaction.tsx create mode 100644 apps/app/hooks/use-comment-reaction.tsx create mode 100644 apps/app/hooks/use-issue-reaction.tsx create mode 100644 apps/app/services/reaction.service.ts create mode 100644 apps/app/types/reaction.d.ts diff --git a/apps/app/components/core/reaction-selector.tsx b/apps/app/components/core/reaction-selector.tsx index 1ad29452fb5..585afa43002 100644 --- a/apps/app/components/core/reaction-selector.tsx +++ b/apps/app/components/core/reaction-selector.tsx @@ -1,34 +1,34 @@ import { Fragment } from "react"; +// headless ui import { Popover, Transition } from "@headlessui/react"; +// helper import { renderEmoji } from "helpers/emoji.helper"; +// icons import { Icon } from "components/ui"; -const emojis = ["128077", "128078", "128516", "128165", "128533", "129505", "9992", "128064"]; +const reactionEmojis = [ + "128077", + "128078", + "128516", + "128165", + "128533", + "129505", + "9992", + "128064", +]; -interface CommonProps { - position?: "top" | "bottom"; +interface Props { size?: "sm" | "md" | "lg"; -} - -interface SingleEmojiProps extends CommonProps { - multiple?: false; - value?: string | null; + position?: "top" | "bottom"; + value?: string | string[] | null; onSelect: (emoji: string) => void; } -interface MultipleEmojiProps extends CommonProps { - multiple: true; - value?: string[] | null; - onSelect: (emoji: string[]) => void; -} - -type Props = SingleEmojiProps | MultipleEmojiProps; - export const ReactionSelector: React.FC = (props) => { - const { value, onSelect, multiple, position, size } = props; + const { value, onSelect, position, size } = props; return ( @@ -37,24 +37,14 @@ export const ReactionSelector: React.FC = (props) => { - {value ? ( - - {renderEmoji(Array.isArray(value) ? value[value.length - 1] : value)} - - ) : ( - - )} + = (props) => { leaveTo="opacity-0 translate-y-1" > -
-
- {emojis.map((emoji) => ( +
+
+ {reactionEmojis.map((emoji) => (
diff --git a/apps/app/components/issues/comment/comment-reaction.tsx b/apps/app/components/issues/comment/comment-reaction.tsx new file mode 100644 index 00000000000..cac73809469 --- /dev/null +++ b/apps/app/components/issues/comment/comment-reaction.tsx @@ -0,0 +1,82 @@ +import React from "react"; + +// next +import { useRouter } from "next/router"; + +// hooks +import useUser from "hooks/use-user"; +import useCommentReaction from "hooks/use-comment-reaction"; +// ui +import { ReactionSelector } from "components/core"; +// helper +import { renderEmoji } from "helpers/emoji.helper"; + +type Props = { + commentId: string; +}; + +export const CommentReaction: React.FC = ({ commentId }) => { + const { user } = useUser(); + + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + commentReactions, + groupedReactions, + handleReactionCreate, + handleReactionDelete, + isLoading, + } = useCommentReaction(workspaceSlug, projectId, commentId); + + const handleReactionClick = (reaction: string) => { + if (!workspaceSlug || !projectId || !commentId) return; + + const isSelected = commentReactions?.some( + (r) => r.actor === user?.id && r.reaction === reaction + ); + + if (isSelected) { + handleReactionDelete(reaction); + } else { + handleReactionCreate(reaction); + } + }; + + return ( +
+ reaction.actor === user?.id) + .map((r) => r.reaction) || [] + } + onSelect={handleReactionClick} + /> + + {Object.keys(groupedReactions || {}).map( + (reaction) => + groupedReactions?.[reaction]?.length && + groupedReactions[reaction].length > 0 && ( + + ) + )} +
+ ); +}; diff --git a/apps/app/components/issues/comment/index.ts b/apps/app/components/issues/comment/index.ts index cf13ca91e39..61ac899ada6 100644 --- a/apps/app/components/issues/comment/index.ts +++ b/apps/app/components/issues/comment/index.ts @@ -1,2 +1,3 @@ export * from "./add-comment"; export * from "./comment-card"; +export * from "./comment-reaction"; diff --git a/apps/app/components/issues/index.ts b/apps/app/components/issues/index.ts index 7e9bff17567..8aae12c40ab 100644 --- a/apps/app/components/issues/index.ts +++ b/apps/app/components/issues/index.ts @@ -14,3 +14,4 @@ export * from "./parent-issues-list-modal"; export * from "./sidebar"; export * from "./sub-issues-list"; export * from "./label"; +export * from "./issue-reaction"; diff --git a/apps/app/components/issues/issue-reaction.tsx b/apps/app/components/issues/issue-reaction.tsx new file mode 100644 index 00000000000..cfe7e4a57ec --- /dev/null +++ b/apps/app/components/issues/issue-reaction.tsx @@ -0,0 +1,66 @@ +import { useRouter } from "next/router"; + +// hooks +import useUserAuth from "hooks/use-user-auth"; +import useIssueReaction from "hooks/use-issue-reaction"; +// components +import { ReactionSelector } from "components/core"; +// string helpers +import { renderEmoji } from "helpers/emoji.helper"; + +export const IssueReaction: React.FC = () => { + const router = useRouter(); + const { workspaceSlug, projectId, issueId } = router.query; + + const { user } = useUserAuth(); + + const { reactions, groupedReactions, handleReactionCreate, handleReactionDelete } = + useIssueReaction(workspaceSlug, projectId, issueId); + + const handleReactionClick = (reaction: string) => { + if (!workspaceSlug || !projectId || !issueId) return; + + const isSelected = reactions?.some((r) => r.actor === user?.id && r.reaction === reaction); + + if (isSelected) { + handleReactionDelete(reaction); + } else { + handleReactionCreate(reaction); + } + }; + + return ( +
+ reaction.actor === user?.id).map((r) => r.reaction) || [] + } + onSelect={handleReactionClick} + /> + + {Object.keys(groupedReactions || {}).map( + (reaction) => + groupedReactions?.[reaction]?.length && + groupedReactions[reaction].length > 0 && ( + + ) + )} +
+ ); +}; diff --git a/apps/app/components/issues/main-content.tsx b/apps/app/components/issues/main-content.tsx index 57052650ea2..4dbb395910a 100644 --- a/apps/app/components/issues/main-content.tsx +++ b/apps/app/components/issues/main-content.tsx @@ -17,6 +17,7 @@ import { IssueAttachments, IssueDescriptionForm, SubIssuesList, + IssueReaction, } from "components/issues"; // ui import { CustomMenu } from "components/ui"; @@ -117,6 +118,9 @@ export const IssueMainContent: React.FC = ({ handleFormSubmit={submitChanges} isAllowed={memberRole.isMember || memberRole.isOwner || !uneditable} /> + + +
diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index 8928c2236c9..45aa662733f 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -243,3 +243,13 @@ export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = ( export const UNREAD_NOTIFICATIONS_COUNT = (workspaceSlug: string) => `UNREAD_NOTIFICATIONS_COUNT_${workspaceSlug.toUpperCase()}`; + +export const ISSUE_REACTION_LIST = (workspaceSlug: string, projectId: string, issueId: string) => + `ISSUE_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${issueId.toUpperCase()}`; + +export const COMMENT_REACTION_LIST = ( + workspaceSlug: string, + projectId: string, + commendId: string +) => + `COMMENT_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${commendId.toUpperCase()}`; diff --git a/apps/app/helpers/emoji.helper.ts b/apps/app/helpers/emoji.helper.ts index f16d0021cc6..5657f0e2528 100644 --- a/apps/app/helpers/emoji.helper.ts +++ b/apps/app/helpers/emoji.helper.ts @@ -23,3 +23,18 @@ export const renderEmoji = (emoji: string) => { return isNaN(parseInt(emoji)) ? emoji : String.fromCodePoint(parseInt(emoji)); }; + +export const groupReactions: (reactions: any[], key: string) => { [key: string]: any[] } = ( + reactions: any, + key: string +) => { + const groupedReactions = reactions.reduce((acc: any, reaction: any) => { + if (!acc[reaction[key]]) { + acc[reaction[key]] = []; + } + acc[reaction[key]].push(reaction); + return acc; + }, {} as { [key: string]: any[] }); + + return groupedReactions; +}; diff --git a/apps/app/hooks/use-comment-reaction.tsx b/apps/app/hooks/use-comment-reaction.tsx new file mode 100644 index 00000000000..d52c65d09fd --- /dev/null +++ b/apps/app/hooks/use-comment-reaction.tsx @@ -0,0 +1,95 @@ +import useSWR from "swr"; + +// fetch keys +import { COMMENT_REACTION_LIST } from "constants/fetch-keys"; + +// services +import reactionService from "services/reaction.service"; + +// helpers +import { groupReactions } from "helpers/emoji.helper"; + +// hooks +import useUser from "./use-user"; + +const useCommentReaction = ( + workspaceSlug?: string | string[] | null, + projectId?: string | string[] | null, + commendId?: string | string[] | null +) => { + const { + data: commentReactions, + mutate: mutateCommentReactions, + error, + } = useSWR( + workspaceSlug && projectId && commendId + ? COMMENT_REACTION_LIST(workspaceSlug.toString(), projectId.toString(), commendId.toString()) + : null, + workspaceSlug && projectId && commendId + ? () => + reactionService.listIssueCommentReactions( + workspaceSlug.toString(), + projectId.toString(), + commendId.toString() + ) + : null + ); + + const user = useUser(); + + const groupedReactions = groupReactions(commentReactions || [], "reaction"); + + /** + * @description Use this function to create user's reaction to an issue. This function will mutate the reactions state. + * @param {string} reaction + * @example handleReactionDelete("123") // 123 -> is emoji hexa-code + */ + + const handleReactionCreate = async (reaction: string) => { + if (!workspaceSlug || !projectId || !commendId) return; + + const data = await reactionService.createIssueCommentReaction( + workspaceSlug.toString(), + projectId.toString(), + commendId.toString(), + { reaction } + ); + + mutateCommentReactions((prev) => [...(prev || []), data]); + }; + + /** + * @description Use this function to delete user's reaction from an issue. This function will mutate the reactions state. + * @param {string} reaction + * @example handleReactionDelete("123") // 123 -> is emoji hexa-code + */ + + const handleReactionDelete = async (reaction: string) => { + if (!workspaceSlug || !projectId || !commendId) return; + + mutateCommentReactions( + (prevData) => + prevData?.filter((r) => r.actor !== user?.user?.id || r.reaction !== reaction) || [] + ); + + await reactionService.deleteIssueCommentReaction( + workspaceSlug.toString(), + projectId.toString(), + commendId.toString(), + reaction + ); + + mutateCommentReactions(); + }; + + return { + isLoading: !commentReactions && !error, + commentReactions, + groupedReactions, + handleReactionCreate, + handleReactionDelete, + mutateCommentReactions, + } as const; +}; + +export default useCommentReaction; diff --git a/apps/app/hooks/use-issue-reaction.tsx b/apps/app/hooks/use-issue-reaction.tsx new file mode 100644 index 00000000000..42a0dacdefa --- /dev/null +++ b/apps/app/hooks/use-issue-reaction.tsx @@ -0,0 +1,96 @@ +import useSWR from "swr"; + +// fetch keys +import { ISSUE_REACTION_LIST } from "constants/fetch-keys"; + +// helpers +import { groupReactions } from "helpers/emoji.helper"; + +// services +import reactionService from "services/reaction.service"; + +// hooks +import useUser from "./use-user"; + +const useIssueReaction = ( + workspaceSlug?: string | string[] | null, + projectId?: string | string[] | null, + issueId?: string | string[] | null +) => { + const user = useUser(); + + const { + data: reactions, + mutate: mutateReaction, + error, + } = useSWR( + workspaceSlug && projectId && issueId + ? ISSUE_REACTION_LIST(workspaceSlug.toString(), projectId.toString(), issueId.toString()) + : null, + workspaceSlug && projectId && issueId + ? () => + reactionService.listIssueReactions( + workspaceSlug.toString(), + projectId.toString(), + issueId.toString() + ) + : null + ); + + const groupedReactions = groupReactions(reactions || [], "reaction"); + + /** + * @description Use this function to create user's reaction to an issue. This function will mutate the reactions state. + * @param {string} reaction + * @example handleReactionCreate("128077") // hexa-code of the emoji + */ + + const handleReactionCreate = async (reaction: string) => { + if (!workspaceSlug || !projectId || !issueId) return; + + const data = await reactionService.createIssueReaction( + workspaceSlug.toString(), + projectId.toString(), + issueId.toString(), + { reaction } + ); + + mutateReaction((prev) => [...(prev || []), data]); + }; + + /** + * @description Use this function to delete user's reaction from an issue. This function will mutate the reactions state. + * @param {string} reaction + * @example handleReactionDelete("123") // 123 -> is emoji hexa-code + */ + + const handleReactionDelete = async (reaction: string) => { + if (!workspaceSlug || !projectId || !issueId) return; + + mutateReaction( + (prevData) => + prevData?.filter((r) => r.actor !== user?.user?.id || r.reaction !== reaction) || [], + false + ); + + await reactionService.deleteIssueReaction( + workspaceSlug.toString(), + projectId.toString(), + issueId.toString(), + reaction + ); + + mutateReaction(); + }; + + return { + isLoading: !reactions && !error, + reactions, + groupedReactions, + handleReactionCreate, + handleReactionDelete, + mutateReaction, + } as const; +}; + +export default useIssueReaction; diff --git a/apps/app/services/reaction.service.ts b/apps/app/services/reaction.service.ts new file mode 100644 index 00000000000..3ba8a83e453 --- /dev/null +++ b/apps/app/services/reaction.service.ts @@ -0,0 +1,145 @@ +// services +import APIService from "services/api.service"; +import trackEventServices from "services/track-event.service"; + +// types +import type { + ICurrentUserResponse, + IssueReaction, + IssueCommentReaction, + IssueReactionForm, + IssueCommentReactionForm, +} from "types"; + +const { NEXT_PUBLIC_API_BASE_URL } = process.env; + +const trackEvent = + process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1"; + +class ReactionService extends APIService { + constructor() { + super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); + } + + async createIssueReaction( + workspaceSlug: string, + projectId: string, + issueId: string, + data: IssueReactionForm, + user?: ICurrentUserResponse + ): Promise { + return this.post( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/reactions/`, + data + ) + .then((response) => { + if (trackEvent) + trackEventServices.trackReactionEvent(response?.data, "ISSUE_REACTION_CREATE", user); + return response?.data; + }) + .catch((error) => { + throw error?.response?.data; + }); + } + + async listIssueReactions( + workspaceSlug: string, + projectId: string, + issueId: string + ): Promise { + return this.get( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/reactions/` + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async deleteIssueReaction( + workspaceSlug: string, + projectId: string, + issueId: string, + reaction: string, + user?: ICurrentUserResponse + ): Promise { + return this.delete( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/reactions/${reaction}/` + ) + .then((response) => { + if (trackEvent) + trackEventServices.trackReactionEvent(response?.data, "ISSUE_REACTION_DELETE", user); + return response?.data; + }) + .catch((error) => { + throw error?.response?.data; + }); + } + + async createIssueCommentReaction( + workspaceSlug: string, + projectId: string, + commentId: string, + data: IssueCommentReactionForm, + user?: ICurrentUserResponse + ): Promise { + return this.post( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/`, + data + ) + .then((response) => { + if (trackEvent) + trackEventServices.trackReactionEvent( + response?.data, + "ISSUE_COMMENT_REACTION_CREATE", + user + ); + return response?.data; + }) + .catch((error) => { + throw error?.response?.data; + }); + } + + async listIssueCommentReactions( + workspaceSlug: string, + projectId: string, + commentId: string + ): Promise { + return this.get( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/` + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + + async deleteIssueCommentReaction( + workspaceSlug: string, + projectId: string, + commentId: string, + reaction: string, + user?: ICurrentUserResponse + ): Promise { + return this.delete( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/${reaction}/` + ) + .then((response) => { + if (trackEvent) + trackEventServices.trackReactionEvent( + response?.data, + "ISSUE_COMMENT_REACTION_DELETE", + user + ); + return response?.data; + }) + .catch((error) => { + throw error?.response?.data; + }); + } +} + +const reactionService = new ReactionService(); + +export default reactionService; diff --git a/apps/app/services/track-event.service.ts b/apps/app/services/track-event.service.ts index bbc6a1a58bc..3da8b843640 100644 --- a/apps/app/services/track-event.service.ts +++ b/apps/app/services/track-event.service.ts @@ -19,6 +19,8 @@ import type { IState, IView, IWorkspace, + IssueCommentReaction, + IssueReaction, } from "types"; type WorkspaceEventType = @@ -110,6 +112,12 @@ type AnalyticsEventType = | "MODULE_CUSTOM_ANALYTICS" | "MODULE_ANALYTICS_EXPORT"; +type ReactionEventType = + | "ISSUE_REACTION_CREATE" + | "ISSUE_COMMENT_REACTION_CREATE" + | "ISSUE_REACTION_DELETE" + | "ISSUE_COMMENT_REACTION_DELETE"; + class TrackEventServices extends APIService { constructor() { super("/"); @@ -799,6 +807,32 @@ class TrackEventServices extends APIService { }, }); } + + async trackReactionEvent( + data: IssueReaction | IssueCommentReaction, + eventName: ReactionEventType, + user: ICurrentUserResponse | undefined + ): Promise { + let payload: any; + if (eventName === "ISSUE_REACTION_DELETE" || eventName === "ISSUE_COMMENT_REACTION_DELETE") + payload = data; + else + payload = { + workspaceId: data?.workspace, + projectId: data?.project, + reaction: data?.reaction, + }; + + return this.request({ + url: "/api/track-event", + method: "POST", + data: { + eventName, + extra: payload, + user: user, + }, + }); + } } const trackEventServices = new TrackEventServices(); diff --git a/apps/app/types/index.d.ts b/apps/app/types/index.d.ts index c2a8efbf1b9..dbd3148269d 100644 --- a/apps/app/types/index.d.ts +++ b/apps/app/types/index.d.ts @@ -17,6 +17,7 @@ export * from "./analytics"; export * from "./calendar"; export * from "./notifications"; export * from "./waitlist"; +export * from "./reaction"; export type NestedKeyOf = { [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object diff --git a/apps/app/types/reaction.d.ts b/apps/app/types/reaction.d.ts new file mode 100644 index 00000000000..9e43496e7bb --- /dev/null +++ b/apps/app/types/reaction.d.ts @@ -0,0 +1,33 @@ +export interface IssueReaction { + id: string; + created_at: Date; + updated_at: Date; + reaction: string; + created_by: string; + updated_by: string; + project: string; + workspace: string; + actor: string; + issue: string; +} + +export interface IssueReactionForm { + reaction: string; +} + +export interface IssueCommentReaction { + id: string; + created_at: Date; + updated_at: Date; + reaction: string; + created_by: string; + updated_by: string; + project: string; + workspace: string; + actor: string; + comment: string; +} + +export interface IssueCommentReactionForm { + reaction: string; +} From 46962f8b1e286c2300433b54888436b85f3e35e8 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 27 Jul 2023 17:12:38 +0530 Subject: [PATCH 3/6] fix: emoji.helper.tsx function --- apps/app/helpers/emoji.helper.ts | 40 ------------------------------- apps/app/helpers/emoji.helper.tsx | 15 ++++++++++++ 2 files changed, 15 insertions(+), 40 deletions(-) delete mode 100644 apps/app/helpers/emoji.helper.ts diff --git a/apps/app/helpers/emoji.helper.ts b/apps/app/helpers/emoji.helper.ts deleted file mode 100644 index 5657f0e2528..00000000000 --- a/apps/app/helpers/emoji.helper.ts +++ /dev/null @@ -1,40 +0,0 @@ -export const getRandomEmoji = () => { - const emojis = [ - "8986", - "9200", - "128204", - "127773", - "127891", - "127947", - "128076", - "128077", - "128187", - "128188", - "128512", - "128522", - "128578", - ]; - - return emojis[Math.floor(Math.random() * emojis.length)]; -}; - -export const renderEmoji = (emoji: string) => { - if (!emoji) return; - - return isNaN(parseInt(emoji)) ? emoji : String.fromCodePoint(parseInt(emoji)); -}; - -export const groupReactions: (reactions: any[], key: string) => { [key: string]: any[] } = ( - reactions: any, - key: string -) => { - const groupedReactions = reactions.reduce((acc: any, reaction: any) => { - if (!acc[reaction[key]]) { - acc[reaction[key]] = []; - } - acc[reaction[key]].push(reaction); - return acc; - }, {} as { [key: string]: any[] }); - - return groupedReactions; -}; diff --git a/apps/app/helpers/emoji.helper.tsx b/apps/app/helpers/emoji.helper.tsx index 7e4acd2ef4f..06bf8ba15a9 100644 --- a/apps/app/helpers/emoji.helper.tsx +++ b/apps/app/helpers/emoji.helper.tsx @@ -36,3 +36,18 @@ export const renderEmoji = ( ); else return isNaN(parseInt(emoji)) ? emoji : String.fromCodePoint(parseInt(emoji)); }; + +export const groupReactions: (reactions: any[], key: string) => { [key: string]: any[] } = ( + reactions: any, + key: string +) => { + const groupedReactions = reactions.reduce((acc: any, reaction: any) => { + if (!acc[reaction[key]]) { + acc[reaction[key]] = []; + } + acc[reaction[key]].push(reaction); + return acc; + }, {} as { [key: string]: any[] }); + + return groupedReactions; +}; From eb1cfe316674c0d605eec0fa7726a15da2f70ec9 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 27 Jul 2023 18:19:54 +0530 Subject: [PATCH 4/6] refactor: reaction not working on inbox issue --- apps/app/components/inbox/inbox-main-content.tsx | 8 ++++++++ .../app/components/issues/comment/comment-card.tsx | 6 +++++- .../components/issues/comment/comment-reaction.tsx | 12 +++++------- apps/app/components/issues/issue-reaction.tsx | 14 +++++++++----- apps/app/components/issues/main-content.tsx | 2 +- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/apps/app/components/inbox/inbox-main-content.tsx b/apps/app/components/inbox/inbox-main-content.tsx index 49a3baf91aa..af123485943 100644 --- a/apps/app/components/inbox/inbox-main-content.tsx +++ b/apps/app/components/inbox/inbox-main-content.tsx @@ -19,6 +19,7 @@ import { IssueActivitySection, IssueDescriptionForm, IssueDetailsSidebar, + IssueReaction, } from "components/issues"; // ui import { Loader } from "components/ui"; @@ -303,6 +304,13 @@ export const InboxMainContent: React.FC = () => { } />
+ + +

Comments/Activity

diff --git a/apps/app/components/issues/comment/comment-card.tsx b/apps/app/components/issues/comment/comment-card.tsx index c93503e6c46..a78be0fa1a2 100644 --- a/apps/app/components/issues/comment/comment-card.tsx +++ b/apps/app/components/issues/comment/comment-card.tsx @@ -140,7 +140,11 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD ref={showEditorRef} /> - +
diff --git a/apps/app/components/issues/comment/comment-reaction.tsx b/apps/app/components/issues/comment/comment-reaction.tsx index cac73809469..d701fa943ec 100644 --- a/apps/app/components/issues/comment/comment-reaction.tsx +++ b/apps/app/components/issues/comment/comment-reaction.tsx @@ -1,8 +1,5 @@ import React from "react"; -// next -import { useRouter } from "next/router"; - // hooks import useUser from "hooks/use-user"; import useCommentReaction from "hooks/use-comment-reaction"; @@ -12,14 +9,15 @@ import { ReactionSelector } from "components/core"; import { renderEmoji } from "helpers/emoji.helper"; type Props = { + workspaceSlug?: string | string[]; + projectId?: string | string[]; commentId: string; }; -export const CommentReaction: React.FC = ({ commentId }) => { - const { user } = useUser(); +export const CommentReaction: React.FC = (props) => { + const { workspaceSlug, projectId, commentId } = props; - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { user } = useUser(); const { commentReactions, diff --git a/apps/app/components/issues/issue-reaction.tsx b/apps/app/components/issues/issue-reaction.tsx index cfe7e4a57ec..d183685fec7 100644 --- a/apps/app/components/issues/issue-reaction.tsx +++ b/apps/app/components/issues/issue-reaction.tsx @@ -1,5 +1,3 @@ -import { useRouter } from "next/router"; - // hooks import useUserAuth from "hooks/use-user-auth"; import useIssueReaction from "hooks/use-issue-reaction"; @@ -8,9 +6,15 @@ import { ReactionSelector } from "components/core"; // string helpers import { renderEmoji } from "helpers/emoji.helper"; -export const IssueReaction: React.FC = () => { - const router = useRouter(); - const { workspaceSlug, projectId, issueId } = router.query; +// types +type Props = { + workspaceSlug?: string | string[]; + projectId?: string | string[]; + issueId?: string | string[]; +}; + +export const IssueReaction: React.FC = (props) => { + const { workspaceSlug, projectId, issueId } = props; const { user } = useUserAuth(); diff --git a/apps/app/components/issues/main-content.tsx b/apps/app/components/issues/main-content.tsx index 4dbb395910a..5010d6d5bd7 100644 --- a/apps/app/components/issues/main-content.tsx +++ b/apps/app/components/issues/main-content.tsx @@ -119,7 +119,7 @@ export const IssueMainContent: React.FC = ({ isAllowed={memberRole.isMember || memberRole.isOwner || !uneditable} /> - +
From f9ff0c56f08e0516beddedc3ad242caa3e4b9f8e Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 27 Jul 2023 19:40:52 +0530 Subject: [PATCH 5/6] fix: user not been passed to create/delete function --- apps/app/components/issues/comment/comment-reaction.tsx | 2 +- apps/app/components/issues/issue-reaction.tsx | 2 +- apps/app/hooks/use-comment-reaction.tsx | 6 ++++-- apps/app/hooks/use-issue-reaction.tsx | 6 ++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/app/components/issues/comment/comment-reaction.tsx b/apps/app/components/issues/comment/comment-reaction.tsx index d701fa943ec..3b77d26f08d 100644 --- a/apps/app/components/issues/comment/comment-reaction.tsx +++ b/apps/app/components/issues/comment/comment-reaction.tsx @@ -70,8 +70,8 @@ export const CommentReaction: React.FC = (props) => { : "bg-custom-background-80" }`} > - {groupedReactions?.[reaction].length} {renderEmoji(reaction)} + {groupedReactions?.[reaction].length} ) )} diff --git a/apps/app/components/issues/issue-reaction.tsx b/apps/app/components/issues/issue-reaction.tsx index d183685fec7..20cce9313d3 100644 --- a/apps/app/components/issues/issue-reaction.tsx +++ b/apps/app/components/issues/issue-reaction.tsx @@ -60,8 +60,8 @@ export const IssueReaction: React.FC = (props) => { : "bg-custom-background-80" }`} > - {groupedReactions?.[reaction].length} {renderEmoji(reaction)} + {groupedReactions?.[reaction].length} ) )} diff --git a/apps/app/hooks/use-comment-reaction.tsx b/apps/app/hooks/use-comment-reaction.tsx index d52c65d09fd..16469f66a4d 100644 --- a/apps/app/hooks/use-comment-reaction.tsx +++ b/apps/app/hooks/use-comment-reaction.tsx @@ -52,7 +52,8 @@ const useCommentReaction = ( workspaceSlug.toString(), projectId.toString(), commendId.toString(), - { reaction } + { reaction }, + user.user ); mutateCommentReactions((prev) => [...(prev || []), data]); @@ -76,7 +77,8 @@ const useCommentReaction = ( workspaceSlug.toString(), projectId.toString(), commendId.toString(), - reaction + reaction, + user.user ); mutateCommentReactions(); diff --git a/apps/app/hooks/use-issue-reaction.tsx b/apps/app/hooks/use-issue-reaction.tsx index 42a0dacdefa..89c65c5ed31 100644 --- a/apps/app/hooks/use-issue-reaction.tsx +++ b/apps/app/hooks/use-issue-reaction.tsx @@ -52,7 +52,8 @@ const useIssueReaction = ( workspaceSlug.toString(), projectId.toString(), issueId.toString(), - { reaction } + { reaction }, + user.user ); mutateReaction((prev) => [...(prev || []), data]); @@ -77,7 +78,8 @@ const useIssueReaction = ( workspaceSlug.toString(), projectId.toString(), issueId.toString(), - reaction + reaction, + user.user ); mutateReaction(); From a4d0f0a9291b8437375cafca9647dab44110fbdd Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Thu, 27 Jul 2023 19:47:48 +0530 Subject: [PATCH 6/6] style: text position & color --- .../app/components/issues/comment/comment-reaction.tsx | 10 +++++++++- apps/app/components/issues/issue-reaction.tsx | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/app/components/issues/comment/comment-reaction.tsx b/apps/app/components/issues/comment/comment-reaction.tsx index 3b77d26f08d..0f77df025c8 100644 --- a/apps/app/components/issues/comment/comment-reaction.tsx +++ b/apps/app/components/issues/comment/comment-reaction.tsx @@ -71,7 +71,15 @@ export const CommentReaction: React.FC = (props) => { }`} > {renderEmoji(reaction)} - {groupedReactions?.[reaction].length} + r.actor === user?.id && r.reaction === reaction) + ? "text-custom-primary-100" + : "" + } + > + {groupedReactions?.[reaction].length}{" "} + ) )} diff --git a/apps/app/components/issues/issue-reaction.tsx b/apps/app/components/issues/issue-reaction.tsx index 20cce9313d3..9277954a48a 100644 --- a/apps/app/components/issues/issue-reaction.tsx +++ b/apps/app/components/issues/issue-reaction.tsx @@ -61,7 +61,15 @@ export const IssueReaction: React.FC = (props) => { }`} > {renderEmoji(reaction)} - {groupedReactions?.[reaction].length} + r.actor === user?.id && r.reaction === reaction) + ? "text-custom-primary-100" + : "" + } + > + {groupedReactions?.[reaction].length}{" "} + ) )}