const Message = styled.div` width: 100%; display: flex; padding-bottom: 1rem; ${({ ownMessage }) => ownMessage ? "justify-content: flex-end;" : "justify-content: flex-start;"} `; const SenderInfoContainer = styled.div` width: 100%; display: flex; align-items: center; column-gap: 0.5rem; display: flex; ${({ ownMessage }) => ownMessage ? "justify-content: flex-end;" : "justify-content: flex-start;"} `; const ProfileIconContainerMsg = styled.div` display: flex; justify-content: center; align-items: center; width: 32px; height: 32px; border-radius: 50%; ${({ id }) => id && `background-color: #111;`} text-align: center; /* Body/Small */ font-family: Helvetica Neue; font-size: 14px; font-style: normal; font-weight: 400; line-height: 150%; /* 21px */ margin-left: -8px; `; const NameContainerSender = styled.div` display: flex; justify-content: start; align-items: center; width: 100%; color: #6c757d; font-size: 14px; font-style: normal; font-weight: 400; line-height: 100%; `; const TimeText = styled.p` color: #adb5bd; font-family: Helvetica Neue; font-size: 14px; font-style: normal; font-weight: 400; line-height: 100%; padding-top: 1rem; `; const MessageText = styled.div` max-width: 400px; position: relative; word-wrap: break-word; display: flex; flex-direction: column; row-gap: 20px; padding: 1rem; color: #fff; font-family: Helvetica Neue; font-size: 16px; font-style: normal; font-weight: 400; line-height: 150%; ${({ ownMessage, clickable }) => ownMessage ? `background-color: #5765F2; border-radius: 8px 0px 8px 8px; ${ clickable && ":hover { background-color: #545CCD; cursor: pointer; }" }` : `background-color: #1E1F28; border-radius: 0px 8px 8px 8px; ${ clickable && ":hover { background-color: #3C3E52; cursor: pointer; }" }`} `; const ReactionsContainer = styled.div` display: flex; position: absolute; z-index: 30; bottom: -2rem; ${({ ownMessage }) => (ownMessage ? "right: 0.5rem;" : "left: 0.5rem;")} column-gap: 0.5rem; font-size: 1.5rem; line-height: 1.75rem; cursor: pointer; background: #0e0e10; border-radius: 4px; padding-left: 2px; padding-right: 2px; padding-top: 2px; padding-bottom: 2px; `; const EmojiContainer = styled.p` border-radius: 4px; :hover { background-color: #5765f2; } text-align: center; display: flex; justify-content: center; align-items: center; `; const ReactedContainer = styled.div` display: flex; position: absolute; top: -0.75rem; right: 0.5rem; column-gap: 0.5rem; font-size: 1rem; line-height: 1rem; cursor: pointer; border-radius: 4px; padding-left: 2px; padding-right: 2px; padding-top: 2px; padding-bottom: 2px; `; const EmojiReactedContainer = styled.div` position: relative; `; const EmojiCountContainer = styled.div` font-size: 0.75rem; line-height: 1rem; position: absolute; bottom: -0.6rem; right: -0.5rem; background-color: #1d1d21; width: 1rem; height: 1rem; border-radius: 50%; text-align: center; `; const ImageContainer = styled.img` border-radius: 4px; `; const reactionsArray = [ { emoji: "😠", title: "Angry Face", }, { emoji: "❤️", title: "Red Heart", }, { emoji: "😀", title: "Grinning Face", }, { emoji: "😂", title: "Face with Tears of Joy", }, { emoji: "👍", title: "Thumbs Up", }, { emoji: "👎", title: "Thumbs Down", }, { emoji: "✅", title: "Check Mark Button", }, ]; const formatTimeAgo = (seconds) => { const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); const weeks = Math.floor(days / 7); const months = Math.floor(weeks / 4); if (months > 0) { return `${months} month${months > 1 ? "s" : ""} ago`; } else if (weeks > 0) { return `${weeks} week${weeks > 1 ? "s" : ""} ago`; } else if (days > 0) { return `${days} day${days > 1 ? "s" : ""} ago`; } else if (hours > 0) { return `${hours} hour${hours > 1 ? "s" : ""} ago`; } else if (minutes > 0) { return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; } else { return `just now`; } }; State.init({ reactionsOpen: false, reactions: reactionsArray, }); const showReactions = () => State.update({ reactionsOpen: true }); const hideReactions = () => State.update({ reactionsOpen: false }); function decrypt() { const nonce = []; let hexString = props.message.nonce; while (hexString.length >= 2) { nonce.push(parseInt(hexString.substring(0, 2), 16)); hexString = hexString.substring(2, hexString.length); } const decipher = Crypto.createDecipheriv("aes-256-cbc", props.key, nonce); let decrypted = decipher.update(props.message.text, "base64", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } const text = decrypt(); const reactionEmojiArray = props.message.reactions ? Object.keys(props.message.reactions).map((reaction) => ({ reaction, accounts: props.message.reactions[reaction], })) : []; const overLayContainer = styled.div` z-index: 40; display: flex; text-align: center; padding-left: 0.5rem; padding-right: 0.5rem; padding-top: 0.25rem; padding-bottom: 0.25rem; background-color: #0e0e10; border: 1px solid #777583; border-radius: 0.375rem; font-size: 16px; padding-left: 24px; margin-top: 0.725rem; color: #fff; `; const storageKey = "lastReportedMessage"; const oldReportedId = Storage.privateGet(storageKey); const overlay = <overLayContainer>Reply in thread</overLayContainer>; return ( <> <Message ownMessage={props.message.sender === context.accountId} key={props.id} > <div> <SenderInfoContainer ownMessage={props.message.sender === context.accountId} > <div> <ProfileIconContainerMsg> <Widget src={`${props.componentOwnerId}/widget/Calimero.Curb.ProfileIcon.UserProfileIcon`} props={{ accountId: props.message.sender, showStatus: false, componentOwnerId: props.componentOwnerId, }} /> </ProfileIconContainerMsg> </div> <div> <NameContainerSender>{props.message.sender}</NameContainerSender> </div> <TimeText> {formatTimeAgo((Date.now() - props.message.timestamp) / 1000)} </TimeText> </SenderInfoContainer> <MessageText onMouseEnter={() => { showReactions(); }} onMouseLeave={() => { hideReactions(); }} ownMessage={props.message.sender === context.accountId} clickable={text.split("$?$").length > 1} > {text.split("$?$")[0]} {state.reactionsOpen && ( <ReactionsContainer ownMessage={props.message.sender === context.accountId} > {state.reactions && ( <> {state.reactions?.map((reaction, id) => ( <EmojiContainer onClick={() => { props.addMessageReaction({ message_id: props.message.id, reaction: reaction.emoji, }); }} key={id} > {reaction.emoji} </EmojiContainer> ))} {!props.isThread && ( <EmojiContainer onClick={() => props.setThread(props.message.id)} > <OverlayTrigger show={state.show} trigger={["hover", "focus"]} delay={{ show: 250, hide: 300 }} placement="auto" overlay={overlay} > <i class="bi bi-reply"></i> </OverlayTrigger> </EmojiContainer> )} </> )} </ReactionsContainer> )} {text.split("$?$").length > 1 && ( <ImageContainer src={text.split("$?$")[1]} alt="uploaded" style={{ maxHeight: "400px", maxWidth: "400px" }} onClick={() => { if (text.split("$?$").length > 1) { props.setImage(text.split("$?$")[1]); } }} /> )} {reactionEmojiArray.length > 0 && ( <ReactedContainer> {reactionEmojiArray?.map((reaction, id) => ( <> {reaction.accounts.length > 0 && ( <EmojiReactedContainer key={id}> {reaction.reaction} <EmojiCountContainer> {reaction.accounts.length.toString()} </EmojiCountContainer> </EmojiReactedContainer> )} </> ))} </ReactedContainer> )} </MessageText> {props.message.thread.length > 0 && ( <Widget src={`${props.componentOwnerId}/widget/Calimero.Curb.Chat.ReplyContainerButton`} props={{ ownMessage: props.message.sender === context.accountId, replyCount: props.message.thread.length, onClick: () => props.setThread(props.message.id), time: formatTimeAgo( (Date.now() - props.message.thread[props.message.thread.length - 1] .timestamp) / 1000 ), }} /> )} </div> </Message> {oldReportedId === props.message.id && props.lastMessageId !== oldReportedId && props.message.sender !== context.accountId && ( <Widget src={`${props.componentOwnerId}/widget/Calimero.Curb.Chat.UnreadBadge`} /> )} </> );