Skip to content
This repository has been archived by the owner on Feb 17, 2025. It is now read-only.

Commit

Permalink
feat: basic tagging functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
arian-garshi authored Sep 18, 2023
1 parent 6a69a55 commit fed4a5a
Show file tree
Hide file tree
Showing 8 changed files with 360 additions and 47 deletions.
35 changes: 16 additions & 19 deletions src/Components/SideSheet/Comments/Components/ClusteredMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,23 @@ const ClusteredMessages: FC<ClusteredMessagesProps> = () => {
</TimeStamp>
</Header>
<div>
{cluster.messages.map((message, messageIndex) => {
console.log(message)
return (
<>
{message.isEdited && (
<Typography variant="meta">
Edited
{" "}
{formatDate(message.modifiedDate || "")}
</Typography>
{cluster.messages.map((message, messageIndex) => (
<>
{message.isEdited && (
<Typography variant="meta">
Edited
{" "}
{formatDate(message.modifiedDate || "")}
</Typography>
)}
<MessageBox
key={`${cluster.userId}-${index}-${messageIndex}`}
messageObject={message}
userId={cluster.userId}
isCurrentUser={isCurrentUser(cluster.userId)}
/>
</>
)
})}
<MessageBox
key={`${cluster.userId}-${index}-${messageIndex}`}
messageObject={message}
userId={cluster.userId}
isCurrentUser={isCurrentUser(cluster.userId)}
/>
</>
))}
</div>
</Container>
))}
Expand Down
96 changes: 86 additions & 10 deletions src/Components/SideSheet/Comments/Components/CommentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@ import { Message } from "../../../../Models/Message"
import InputController from "./InputController"
import { ViewContext } from "../../../../Context/ViewContext"
import ClusteredMessages from "./ClusteredMessages"
import TagDropDown from "./TagDropDown"
import { processMessageInput } from "../../../../utils/helpers"

const Controls = styled.div`
position: sticky;
bottom: 0;
width: 100%;
box-sizing: border-box;
`
const Container = styled.div`
display: flex;
flex-direction: column;
Expand All @@ -35,14 +44,48 @@ const CommentView: React.FC<CommentViewProps> = ({
currentProperty,
}) => {
const [newMessage, setNewMessage] = useState<Message>()
const [taggedUsers, setTaggedUsers] = useState<string[]>([])
const [searchTerm, setSearchTerm] = useState<string>("")
const [showTagDropDown, setShowTagDropDown] = useState<boolean>(false)
const {
activeTagData, conversations, setConversations, activeConversation, setActiveConversation,
activeTagData,
conversations,
setConversations,
activeConversation,
setActiveConversation,
} = useContext(ViewContext)

const getConversationForProperty = (property: string) => (
conversations.find((conversation) => conversation.property?.toUpperCase() === property.toUpperCase())
)

const dummyData = [
{
id: "1",
displayName: "Henrik Hansen",
accountType: "Consultant",
status: "Active",
},
{
id: "2",
displayName: "Peter Jensen",
accountType: "Consultant",
status: "Active",
},
{
id: "3",
displayName: "Jesper Gudbransen",
accountType: "Consultant",
status: "inactive",
},
{
id: "4",
displayName: "Mikkel Eriksen",
accountType: "Consultant",
status: "inactive",
},
]

useEffect(() => {
(async () => {
try {
Expand All @@ -63,12 +106,18 @@ const CommentView: React.FC<CommentViewProps> = ({
})()
}, [currentProperty])

const handleMessageChange = (
event: React.ChangeEvent<HTMLTextAreaElement>,
) => {
const handleTagSelected = (displayName: string, userId: string) => {
const commentText = newMessage?.text ?? ""
const lastAtPos = commentText.lastIndexOf("@")
const beforeAt = commentText.substring(0, lastAtPos)
const afterAt = commentText.substring(lastAtPos + 1).replace(/^\S+/, "") // Removes the word right after the "@"

const newCommentText = `${beforeAt}<span data-mention="${userId}" contenteditable="false">${displayName}</span>&nbsp;${afterAt}`
const message = { ...newMessage }
message.text = event.target.value
message.text = newCommentText
setNewMessage(message)
setShowTagDropDown(false)
setSearchTerm("")
}

const createConversation = async () => {
Expand All @@ -92,6 +141,9 @@ const CommentView: React.FC<CommentViewProps> = ({

const addMessage = async () => {
const message = { ...newMessage }
const { processedString, mentions } = processMessageInput(newMessage?.text ?? "")
console.log("mentions: ", mentions) // to be used for tagging users in the future
message.text = processedString
try {
const service = await GetConversationService()
const savedMessage = await service.addMessage(activeTagData?.review?.id ?? "", activeConversation?.id ?? "", message)
Expand All @@ -106,6 +158,8 @@ const CommentView: React.FC<CommentViewProps> = ({
console.error(`Error creating comment: ${error}`)
}
setNewMessage(undefined)
setTaggedUsers([])
setSearchTerm("")
}

const handleSubmit = async () => {
Expand All @@ -116,16 +170,38 @@ const CommentView: React.FC<CommentViewProps> = ({
}
}

useEffect(() => {
console.log("newMessage: ", newMessage)
}, [newMessage])

useEffect(() => {
console.log("taggedUsers: ", taggedUsers)
}, [taggedUsers])

return (
<Container>
<ConversationDiv>
<ClusteredMessages />
</ConversationDiv>
<InputController
value={newMessage?.text ?? ""}
handleCommentChange={handleMessageChange}
handleSubmit={handleSubmit}
/>
<Controls>
{showTagDropDown && (
<TagDropDown
SearchTerm={searchTerm}
setTaggedUsers={setTaggedUsers}
onTagSelected={handleTagSelected}
dummyData={dummyData}
/>
)}

<InputController
handleSubmit={handleSubmit}
taggedUsers={taggedUsers}
newMessage={newMessage}
setNewMessage={setNewMessage}
setSearchTerm={setSearchTerm}
setShowTagDropDown={setShowTagDropDown}
/>
</Controls>
</Container>
)
}
Expand Down
33 changes: 18 additions & 15 deletions src/Components/SideSheet/Comments/Components/InputController.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React, { FC } from "react"
import {
Input, Button, Icon, Checkbox,
Button, Icon, Checkbox,
} from "@equinor/eds-core-react"
import styled from "styled-components"
import { send } from "@equinor/eds-icons"
import InputField from "./InputField"

const Controls = styled.div`
width: 100%;
padding: 30px 15px 10px 15px;
position: sticky;
bottom: 0;
background-color: white;
border-top: 1px solid LightGray;
display: flex;
Expand All @@ -24,24 +22,29 @@ const InputButtonWrapper = styled.div`
`

interface InputControllerProps {
value: string
handleCommentChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void
handleSubmit: () => void
setSearchTerm: React.Dispatch<React.SetStateAction<string>>
setShowTagDropDown: React.Dispatch<React.SetStateAction<boolean>>
newMessage: any
setNewMessage: React.Dispatch<React.SetStateAction<any>>
taggedUsers: string[]
}

const InputController: FC<InputControllerProps> = ({
value,
handleCommentChange,
handleSubmit,
setShowTagDropDown,
setSearchTerm,
newMessage,
setNewMessage,
taggedUsers,
}) => (
<Controls>
<Input
as="textarea"
type="text"
placeholder="Write a comment..."
onChange={handleCommentChange}
value={value}
rows={3}
<InputField
setSearchTerm={setSearchTerm}
setShowTagDropDown={setShowTagDropDown}
newReviewComment={newMessage}
setNewReviewComment={setNewMessage}
taggedUsers={taggedUsers}
/>
<InputButtonWrapper>
<Checkbox label="Send to contractor" />
Expand Down
121 changes: 121 additions & 0 deletions src/Components/SideSheet/Comments/Components/InputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, {
useRef, useState, useEffect, MutableRefObject,
} from "react"
import styled from "styled-components"

const StyledDiv = styled.div`
background-color: #F7F7F7;
border-bottom: 1px solid #6F6F6F;
cursor: text;
padding: 8px;
`

const StyledP = styled.p<{ isPlaceholder: boolean }>`
color: ${({ isPlaceholder }) => (isPlaceholder ? "grey" : "black")};
margin: 0;
min-height: 18px;
&:focus {
outline: none;
}
span {
color: #007079;
font-weight: 500;
`

interface Props {
placeholder?: string
setSearchTerm: React.Dispatch<React.SetStateAction<string>>
setShowTagDropDown: React.Dispatch<React.SetStateAction<boolean>>
newReviewComment: any
setNewReviewComment: React.Dispatch<React.SetStateAction<any>>
taggedUsers: string[]
}

const InputField: React.FC<Props> = ({
placeholder = "Write a comment...",
setSearchTerm,
setShowTagDropDown,
newReviewComment,
setNewReviewComment,
taggedUsers,
}) => {
const pRef = useRef<HTMLParagraphElement>(null)
const [isPlaceholderShown, setIsPlaceholderShown] = useState(true)

useEffect(() => {
if (pRef.current) {
console.log("re-rendering with the text: ", newReviewComment?.text)
pRef.current.innerHTML = newReviewComment?.text || ""
}
}, [taggedUsers])

useEffect(() => {
if (pRef.current && isPlaceholderShown) {
pRef.current.innerHTML = placeholder
}
}, [isPlaceholderShown, placeholder])

const handleCommentChange = (commentText: string) => {
const lastAtPos = commentText.lastIndexOf("@")
const shouldShowDropdown = lastAtPos !== -1

setShowTagDropDown(shouldShowDropdown)

if (shouldShowDropdown) {
const termAfterAt = commentText.slice(lastAtPos + 1)
setSearchTerm(termAfterAt)
} else {
setSearchTerm("")
}

const comment = { ...newReviewComment, text: commentText }
setNewReviewComment(comment)
}

const handleFocus = () => {
if (pRef.current) {
pRef.current.contentEditable = "true"
pRef.current.focus()
if (isPlaceholderShown) {
pRef.current.innerText = ""
}
}
}

const handleBlur = () => {
if (pRef.current) {
pRef.current.contentEditable = "false"
const content = pRef.current.innerHTML
handleCommentChange(content)
if (content.trim() === "") {
setIsPlaceholderShown(true)
pRef.current.innerHTML = placeholder
} else {
setIsPlaceholderShown(false)
}
}
}
const handleInput = () => {
if (pRef.current) {
const content = pRef.current.innerHTML
if (content !== placeholder) {
handleCommentChange(content)
}
}
}

return (
<StyledDiv onClick={handleFocus}>
<StyledP
ref={pRef}
onBlur={handleBlur}
onInput={handleInput}
isPlaceholder={isPlaceholderShown}
/>
</StyledDiv>
)
}

export default InputField
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Message } from "../../../../Models/Message"
import { GetConversationService } from "../../../../api/ConversationService"
import { Conversation } from "../../../../Models/Conversation"
import { ViewContext } from "../../../../Context/ViewContext"
import { unescapeHtmlEntities } from "../../../../utils/helpers"

const CommentText = styled(Typography)`
margin: 10px 0;
Expand Down Expand Up @@ -160,7 +161,7 @@ const RenderComment: FC<RenderCommentProps> = ({
onMouseOut={handleClose}
>
{
comment.softDeleted ? "Message deleted by user" : comment.text
comment.softDeleted ? "Message deleted by user" : unescapeHtmlEntities(comment.text || "")
}
</CommentText>
<Popover
Expand Down
Loading

0 comments on commit fed4a5a

Please sign in to comment.