From b9d2722e41067f218fd3adf26a2dd46d412e11a5 Mon Sep 17 00:00:00 2001 From: Devansh Date: Mon, 16 Sep 2024 18:04:11 +0530 Subject: [PATCH 01/76] Fix: emoji parsing issue in file description --- .../react/src/views/AttachmentPreview/AttachmentPreview.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 236c3ec1ba..209c9e33e8 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -6,6 +6,7 @@ import CheckPreviewType from './CheckPreviewType'; import RCContext from '../../context/RCInstance'; import { useMessageStore } from '../../store'; import getAttachmentPreviewStyles from './AttachmentPreview.styles'; +import { parseEmoji } from '../../lib/emoji'; const AttachmentPreview = () => { const { RCInstance, ECOptions } = useContext(RCContext); @@ -25,7 +26,7 @@ const AttachmentPreview = () => { }; const handleFileDescription = (e) => { - setFileDescription(e.target.value); + setFileDescription(parseEmoji(e.target.value)); }; const submit = async () => { From 5cdba75ff9fb58f967e896a38f0c843142d10b45 Mon Sep 17 00:00:00 2001 From: Smriti Doneria Date: Wed, 18 Sep 2024 22:28:39 +0530 Subject: [PATCH 02/76] fix --- packages/react/src/views/ChatHeader/ChatHeader.js | 7 ++++++- .../src/views/ReportMessage/MessageReportWindow.js | 10 +++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index d9b6b8461b..0e46b9ccdd 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -93,8 +93,13 @@ const ChatHeader = ({ const headerTitle = useMessageStore((state) => state.headerTitle); const filtered = useMessageStore((state) => state.filtered); const setFilter = useMessageStore((state) => state.setFilter); - const threadTitle = useMessageStore((state) => state.threadMainMessage?.msg); + const isThreadOpen = useMessageStore((state) => state.isThreadOpen); + const threadMainMessage = useMessageStore((state) => state.threadMainMessage); + const threadTitle = + threadMainMessage?.msg || + (threadMainMessage?.file ? threadMainMessage.file.name : ''); + const closeThread = useMessageStore((state) => state.closeThread); const setShowMembers = useMemberStore((state) => state.setShowMembers); diff --git a/packages/react/src/views/ReportMessage/MessageReportWindow.js b/packages/react/src/views/ReportMessage/MessageReportWindow.js index 96bf234353..0f5e9644e9 100644 --- a/packages/react/src/views/ReportMessage/MessageReportWindow.js +++ b/packages/react/src/views/ReportMessage/MessageReportWindow.js @@ -7,9 +7,13 @@ import styles from './ReportMessage.styles'; const MessageReportWindow = ({ messageId }) => { const [reportDescription, setDescription] = useState(''); - const messages = useMessageStore((state) => state.messages); - const messageText = messages.filter((message) => message._id === messageId)[0] - ?.msg; + const messages = useMessageStore((state) => state.messages) || []; + const threadMessages = useMessageStore((state) => state.threadMessages) || []; + const allMessages = [...messages, ...threadMessages]; + const messageText = allMessages.filter( + (message) => message._id === messageId + )[0]?.msg; + console.log('messagetext', messageText); return ( Date: Sat, 21 Sep 2024 22:19:48 +0530 Subject: [PATCH 03/76] Starred Model fix --- .../src/views/MessageAggregators/common/MessageAggregator.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index a49f547e18..81a277084e 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -26,9 +26,11 @@ export const MessageAggregator = ({ const styles = getMessageAggregatorStyles(theme); const setExclusiveState = useSetExclusiveState(); const messages = useMessageStore((state) => state.messages); + const threadMessages=useMessageStore((state)=>state.threadMessages)|| []; + const allMessages=[...messages,...threadMessages]; const [messageRendered, setMessageRendered] = useState(false); const { loading, messageList } = useSetMessageList( - searchFiltered || messages, + searchFiltered || allMessages, shouldRender ); From 5c78a16e76ce40f76b9f68da06375e04d267997b Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Thu, 26 Sep 2024 00:50:59 +0530 Subject: [PATCH 04/76] removed console.log --- packages/react/src/views/ReportMessage/MessageReportWindow.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/views/ReportMessage/MessageReportWindow.js b/packages/react/src/views/ReportMessage/MessageReportWindow.js index 0f5e9644e9..74bc1fb814 100644 --- a/packages/react/src/views/ReportMessage/MessageReportWindow.js +++ b/packages/react/src/views/ReportMessage/MessageReportWindow.js @@ -13,7 +13,6 @@ const MessageReportWindow = ({ messageId }) => { const messageText = allMessages.filter( (message) => message._id === messageId )[0]?.msg; - console.log('messagetext', messageText); return ( Date: Fri, 27 Sep 2024 21:53:31 +0530 Subject: [PATCH 05/76] prettier --- .../src/views/MessageAggregators/common/MessageAggregator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 81a277084e..9b9c573066 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -26,8 +26,8 @@ export const MessageAggregator = ({ const styles = getMessageAggregatorStyles(theme); const setExclusiveState = useSetExclusiveState(); const messages = useMessageStore((state) => state.messages); - const threadMessages=useMessageStore((state)=>state.threadMessages)|| []; - const allMessages=[...messages,...threadMessages]; + const threadMessages = useMessageStore((state) => state.threadMessages) || []; + const allMessages = [...messages, ...threadMessages]; const [messageRendered, setMessageRendered] = useState(false); const { loading, messageList } = useSetMessageList( searchFiltered || allMessages, From 13d807e7f781e298c3839179f3e16ec55c9747e4 Mon Sep 17 00:00:00 2001 From: Devansh Kansagra <125076549+devanshkansagra@users.noreply.github.com> Date: Sat, 5 Oct 2024 18:25:48 +0530 Subject: [PATCH 06/76] Combine build steps for react, ui-elements, and layout_editor packages (#637) * Combine build steps for react, ui-elements, and layout_editor packages * Small change in build-lint --- .github/workflows/build-and-lint.yml | 2 +- .github/workflows/deploy.yml | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-lint.yml b/.github/workflows/build-and-lint.yml index d92c4001a5..375ec8c3af 100644 --- a/.github/workflows/build-and-lint.yml +++ b/.github/workflows/build-and-lint.yml @@ -34,7 +34,7 @@ jobs: ${{ runner.os }}-yarn- - name: Install dependencies - run: yarn + run: yarn install - name: Format check run: yarn format:check diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3099455b0c..b3b61cc78c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -30,19 +30,10 @@ jobs: node-version: "16.19.0" - name: Install Dependencies - run: yarn - - - name: Build Storybook - run: yarn build:storybook - working-directory: packages/react - - - name: Build UI-Elements - run: yarn build:storybook - working-directory: packages/ui-elements + run: yarn install - - name: Build Layout Editor - run: npm run build - working-directory: packages/layout_editor + - name: Build packages + run: yarn build && yarn build:storybook - name: Setup Node.js for Docs uses: actions/setup-node@v4 From 3657c15f23917c11d4508694e9e7433fca06a136 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sat, 5 Oct 2024 18:28:18 +0530 Subject: [PATCH 07/76] Changed workflow name --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b3b61cc78c..4055f20ca6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Build and Publish Storybook to GitHub Pages +name: Build and Publish on: push: From ebca321b2fa034376c8100cf381637fa88df8538 Mon Sep 17 00:00:00 2001 From: Devansh Kansagra <125076549+devanshkansagra@users.noreply.github.com> Date: Sun, 6 Oct 2024 12:32:46 +0530 Subject: [PATCH 08/76] Added Feature to deploy previews of pull requests (#638) * Added Feature to deploy previews of pull requests * Removed environment variables from build-lint workflow * Modified the changes mentioned in the code-review --- .github/workflows/build-pr.yml | 65 +++++++++++++++++++++++++++ .github/workflows/deploy-pr.yml | 36 +++++++++++++++ .github/workflows/pr-cleanup.yml | 34 ++++++++++++++ packages/docs/docusaurus.config.js | 2 +- packages/layout_editor/vite.config.ts | 2 +- 5 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build-pr.yml create mode 100644 .github/workflows/deploy-pr.yml create mode 100644 .github/workflows/pr-cleanup.yml diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml new file mode 100644 index 0000000000..66c97af32c --- /dev/null +++ b/.github/workflows/build-pr.yml @@ -0,0 +1,65 @@ +name: Build PR-Preview + +on: + pull_request_review: + types: submitted + +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: true + +env: + LAYOUT_EDITOR_BASE_URL: "/EmbeddedChat/pulls/pr-${{github.event.pull_request.number}}/layout_editor" + DOCS_BASE_URL: "/EmbeddedChat/pulls/pr-${{github.event.pull_request.number}}/docs" + STORYBOOK_RC_HOST: "https://demo.qa.rocket.chat" + +jobs: + build: + if: github.event.review.state == 'approved' && (github.event.review.author_association == 'COLLABORATOR' || github.event.review.author_association == 'OWNER') + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "16.19.0" + + - name: Install Dependencies + run: yarn install + + - name: Build packages + run: yarn build && yarn build:storybook + + - name: Setup Node.js for Docs + uses: actions/setup-node@v4 + with: + node-version: "18.x" + + - name: "Install dependencies for docs" + run: yarn install + working-directory: packages/docs/ + + - name: Build Docs + run: yarn build + working-directory: packages/docs/ + + - name: Prepare Build Folder + run: | + mkdir -p build/pulls/pr-${{github.event.pull_request.number}}/ + mkdir -p build/pulls/pr-${{github.event.pull_request.number}}/ui-elements + mkdir -p build/pulls/pr-${{github.event.pull_request.number}}/layout_editor + mkdir -p build/pulls/pr-${{github.event.pull_request.number}}/docs + + mv -v packages/react/storybook-static/* build/pulls/pr-${{github.event.pull_request.number}}/ + mv -v packages/ui-elements/storybook-static/* build/pulls/pr-${{github.event.pull_request.number}}/ui-elements/ + mv -v packages/layout_editor/dist/* build/pulls/pr-${{github.event.pull_request.number}}/layout_editor/ + mv -v packages/docs/build/* build/pulls/pr-${{github.event.pull_request.number}}/docs/ + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: github-pages + path: build/ diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml new file mode 100644 index 0000000000..4f687507d3 --- /dev/null +++ b/.github/workflows/deploy-pr.yml @@ -0,0 +1,36 @@ +name: Deploy PR-Preview + +on: + workflow_run: + workflows: ["Build PR-Preview"] + types: + - completed + +permissions: + contents: write + pages: write + +jobs: + deploy: + if: github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@v4 + with: + name: github-pages + path: build/ + github-token: ${{github.token}} + repository: ${{github.repository}} + run-id: ${{github.event.workflow_run.id}} + + - name: Deploy to GitHub Pages + uses: crazy-max/ghaction-github-pages@v2 + with: + target_branch: gh-deploy + build_dir: build/ + commit_message: "Deploy to Github Pages" + jekyll: false + keep_history: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-cleanup.yml b/.github/workflows/pr-cleanup.yml new file mode 100644 index 0000000000..c5b878373c --- /dev/null +++ b/.github/workflows/pr-cleanup.yml @@ -0,0 +1,34 @@ +name: Pull Request Cleanup +on: + pull_request_target: + types: [closed] + +jobs: + cleanup: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + ref: gh-deploy + + - name: Check if Deployment Exists + id: check_deployment + run: | + if [ -d "pulls/pr-${{ github.event.pull_request.number }}" ]; then + echo "deployment_exists=true" >> $GITHUB_ENV + else + echo "deployment_exists=false" >> $GITHUB_ENV + fi + + - name: Remove Deployment + if: env.deployment_exists == 'true' + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git fetch origin gh-deploy + git checkout gh-deploy + git rm -r pulls/pr-${{github.event.pull_request.number}} + git commit -m "Remove deployment for PR #${{github.event.pull_request.number}}" + git push origin gh-deploy diff --git a/packages/docs/docusaurus.config.js b/packages/docs/docusaurus.config.js index f9073bfa8e..1201dae919 100644 --- a/packages/docs/docusaurus.config.js +++ b/packages/docs/docusaurus.config.js @@ -17,7 +17,7 @@ const config = { url: "https://rocketchat.github.io/", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: "/EmbeddedChat/docs/", + baseUrl: process.env.DOCS_BASE_URL || "/EmbeddedChat/docs/", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. diff --git a/packages/layout_editor/vite.config.ts b/packages/layout_editor/vite.config.ts index 2b9daff2d3..b6ab8acd24 100644 --- a/packages/layout_editor/vite.config.ts +++ b/packages/layout_editor/vite.config.ts @@ -11,5 +11,5 @@ export default defineConfig({ }, }), ], - base: "/EmbeddedChat/layout_editor" + base: process.env.LAYOUT_EDITOR_BASE_URL || '/EmbeddedChat/layout_editor', }); From 9187d953545ad04199682a44080a9d9b205470da Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sun, 6 Oct 2024 12:54:20 +0530 Subject: [PATCH 09/76] added pr test info in pr template --- .github/pull_request_template.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 337559b404..8a4d4f38d1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,3 +9,9 @@ Fixes # (issue) ## Video/Screenshots + +## PR Test Details + +**Note**: The PR will be ready for live testing at https://rocketchat.github.io/EmbeddedChat/pulls/pr- after approval. + + From d21255c9e1d88e0ddae73c052396cfbda7b306bf Mon Sep 17 00:00:00 2001 From: Rahul Singh Thakur <65606499+Barrylimarti@users.noreply.github.com> Date: Sat, 2 Nov 2024 21:44:15 +0530 Subject: [PATCH 10/76] [fix] Re-rendering fixed while opening pinned and starred messages. (#644) * added memoization to stop rerenders * added memoization for allmessage and shouldrender * fixed linting errors and imports --- packages/react/src/hooks/useSetMessageList.js | 14 ++++++-------- .../views/MessageAggregators/StarredMessages.js | 13 ++++++++----- .../MessageAggregators/common/MessageAggregator.js | 7 +++++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/react/src/hooks/useSetMessageList.js b/packages/react/src/hooks/useSetMessageList.js index a2b3917d59..69b4bef7cf 100644 --- a/packages/react/src/hooks/useSetMessageList.js +++ b/packages/react/src/hooks/useSetMessageList.js @@ -1,16 +1,14 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; export const useSetMessageList = (messages, shouldRender) => { const [loading, setLoading] = useState(true); - const [messageList, setMessageList] = useState([]); - useEffect(() => { - setLoading(true); - const filteredMessages = messages.filter((message) => - shouldRender(message) - ); + const messageList = useMemo( + () => messages.filter(shouldRender), + [messages, shouldRender] + ); - setMessageList(filteredMessages); + useEffect(() => { setLoading(false); }, [messages, shouldRender]); diff --git a/packages/react/src/views/MessageAggregators/StarredMessages.js b/packages/react/src/views/MessageAggregators/StarredMessages.js index 9a79fa0ebe..b7d77b864e 100644 --- a/packages/react/src/views/MessageAggregators/StarredMessages.js +++ b/packages/react/src/views/MessageAggregators/StarredMessages.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { useComponentOverrides } from '@embeddedchat/ui-elements'; import { useUserStore } from '../../store'; import { MessageAggregator } from './common/MessageAggregator'; @@ -7,15 +7,18 @@ const StarredMessages = () => { const authenticatedUserId = useUserStore((state) => state.userId); const { variantOverrides } = useComponentOverrides('StarredMessages'); const viewType = variantOverrides.viewType || 'Sidebar'; + const shouldRender = useCallback( + (msg) => + msg.starred && + msg.starred.some((star) => star._id === authenticatedUserId), + [authenticatedUserId] + ); return ( - msg.starred && - msg.starred.some((star) => star._id === authenticatedUserId) - } + shouldRender={shouldRender} viewType={viewType} /> ); diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 9b9c573066..079582e86a 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import { isSameDay, format } from 'date-fns'; import { Box, Sidebar, Popup, useTheme } from '@embeddedchat/ui-elements'; import { MessageDivider } from '../../Message/MessageDivider'; @@ -27,7 +27,10 @@ export const MessageAggregator = ({ const setExclusiveState = useSetExclusiveState(); const messages = useMessageStore((state) => state.messages); const threadMessages = useMessageStore((state) => state.threadMessages) || []; - const allMessages = [...messages, ...threadMessages]; + const allMessages = useMemo( + () => [...messages, ...threadMessages], + [messages, threadMessages] + ); const [messageRendered, setMessageRendered] = useState(false); const { loading, messageList } = useSetMessageList( searchFiltered || allMessages, From faf9fb76b35bc19bf1f75e56b28b7b24a8a7d30a Mon Sep 17 00:00:00 2001 From: Smriti Doneria Date: Sat, 2 Nov 2024 21:49:44 +0530 Subject: [PATCH 11/76] fixed markdown rendering in report message (#611) * fix * formatting fix * fix * Fiexed lint issues --- .../react/src/views/ReportMessage/MessageReportWindow.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/react/src/views/ReportMessage/MessageReportWindow.js b/packages/react/src/views/ReportMessage/MessageReportWindow.js index 74bc1fb814..33957e72ea 100644 --- a/packages/react/src/views/ReportMessage/MessageReportWindow.js +++ b/packages/react/src/views/ReportMessage/MessageReportWindow.js @@ -2,17 +2,10 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Box, Input } from '@embeddedchat/ui-elements'; import ReportWindowButtons from './ReportWindowButtons'; -import { useMessageStore } from '../../store'; import styles from './ReportMessage.styles'; const MessageReportWindow = ({ messageId }) => { const [reportDescription, setDescription] = useState(''); - const messages = useMessageStore((state) => state.messages) || []; - const threadMessages = useMessageStore((state) => state.threadMessages) || []; - const allMessages = [...messages, ...threadMessages]; - const messageText = allMessages.filter( - (message) => message._id === messageId - )[0]?.msg; return ( { reportDescription={reportDescription} messageId={messageId} > - {JSON.stringify(messageText)} Date: Mon, 4 Nov 2024 22:20:27 +0530 Subject: [PATCH 12/76] added instruction to replace pr num --- .github/pull_request_template.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8a4d4f38d1..ccf362d516 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -12,6 +12,4 @@ Fixes # (issue) ## PR Test Details -**Note**: The PR will be ready for live testing at https://rocketchat.github.io/EmbeddedChat/pulls/pr- after approval. - - +**Note**: The PR will be ready for live testing at https://rocketchat.github.io/EmbeddedChat/pulls/pr- after approval. Contributors are requested to replace `` with the actual PR number. From 36534fc3fd7e5f7824a1c316878a3c7e54828eed Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:36:32 +0530 Subject: [PATCH 13/76] Prevent Quote Message Overflow (#659) * prevent quote message overflow * Removed sidebar open checking and added styling to Quote Message Box --- packages/react/src/views/ChatInput/ChatInput.js | 6 +++++- .../react/src/views/QuoteMessage/QuoteMessage.styles.js | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 99c1c8b4f7..ba9f569b2f 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -423,7 +423,11 @@ const ChatInput = ({ scrollToBottom }) => { return ( - + {(quoteMessage.msg || quoteMessage.attachments) && ( )} diff --git a/packages/react/src/views/QuoteMessage/QuoteMessage.styles.js b/packages/react/src/views/QuoteMessage/QuoteMessage.styles.js index 5ffe32dd2e..1b6619ea95 100644 --- a/packages/react/src/views/QuoteMessage/QuoteMessage.styles.js +++ b/packages/react/src/views/QuoteMessage/QuoteMessage.styles.js @@ -12,6 +12,8 @@ const getQuoteMessageStyles = (theme) => { z-index: 1200; border: 1px solid ${theme.colors.border}; border-radius: ${theme.radius}; + max-width: 100%; + box-sizing: border-box; `, avatarContainer: css` @@ -22,6 +24,10 @@ const getQuoteMessageStyles = (theme) => { message: css` padding: 0.25rem; + overflow-wrap: break-word; + word-break: break-word; + white-space: normal; + width: 100%; `, actionBtn: css` From e898504343afd7c87dd60afcdb9aaffc033a53ac Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sun, 10 Nov 2024 15:39:38 +0530 Subject: [PATCH 14/76] Trim Whitespaces in Username/Email Fields in EmbeddedChat Login Form (#657) * trim whitespaces in username/email fields to prevent login issues * Remove extra .trim() --- packages/api/src/EmbeddedChatApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 574370a5b1..021a82c4db 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -121,12 +121,12 @@ export default class EmbeddedChatApi { let credentials; if (!code) { credentials = credentials = { - user: userOrEmail, + user: userOrEmail.trim(), password, }; } else { credentials = { - user: userOrEmail, + user: userOrEmail.trim(), password, code, }; From afa6b81bafb150fa960a561b9a28fdb819aa8019 Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sun, 10 Nov 2024 21:30:55 +0530 Subject: [PATCH 15/76] Fix: Display emoji in visual format and enable send icon on emoji selection (#663) * Fix: Display emoji in visual format and enable send icon on emoji selection * Revised code --- packages/react/src/views/ChatInput/ChatInput.js | 15 +++++++++------ .../views/ChatInput/ChatInputFormattingToolbar.js | 7 ++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index ba9f569b2f..eae9573aed 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -25,7 +25,6 @@ import useAttachmentWindowStore from '../../store/attachmentwindow'; import MembersList from '../Mentions/MembersList'; import { TypingUsers } from '../TypingUsers'; import createPendingMessage from '../../lib/createPendingMessage'; -import { parseEmoji } from '../../lib/emoji'; import { CommandsList } from '../CommandList'; import useSettingsStore from '../../store/settingsStore'; import ChannelState from '../ChannelState/ChannelState'; @@ -34,6 +33,7 @@ import { getChatInputStyles } from './ChatInput.styles'; import useShowCommands from '../../hooks/useShowCommands'; import useSearchMentionUser from '../../hooks/useSearchMentionUser'; import formatSelection from '../../lib/formatSelection'; +import { parseEmoji } from '../../lib/emoji'; const ChatInput = ({ scrollToBottom }) => { const { styleOverrides, classNames } = useComponentOverrides('ChatInput'); @@ -362,14 +362,16 @@ const ChatInput = ({ scrollToBottom }) => { setData(event.target.files[0]); }; - const onTextChange = (e) => { + const onTextChange = (e, val) => { sendTypingStart(); - const message = e.target.value; + const message = val || e.target.value; messageRef.current.value = parseEmoji(message); setDisableButton(!messageRef.current.value.length); - handleNewLine(e, false); - searchMentionUser(message); - showCommands(e); + if (e !== null) { + handleNewLine(e, false); + searchMentionUser(message); + showCommands(e); + } }; const handleFocus = () => { @@ -532,6 +534,7 @@ const ChatInput = ({ scrollToBottom }) => { )} diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index 634b2255af..fd96f50363 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -19,6 +19,7 @@ import formatSelection from '../../lib/formatSelection'; const ChatInputFormattingToolbar = ({ messageRef, inputRef, + triggerButton, optionConfig = { surfaceItems: ['emoji', 'formatter', 'audio', 'video', 'file'], formatters: ['bold', 'italic', 'strike', 'code', 'multiline'], @@ -46,7 +47,11 @@ const ChatInputFormattingToolbar = ({ const handleEmojiClick = (emojiEvent) => { const [emoji] = emojiEvent.names; - messageRef.current.value += ` :${emoji.replace(/[\s-]+/g, '_')}: `; + const message = `${messageRef.current.value} :${emoji.replace( + /[\s-]+/g, + '_' + )}: `; + triggerButton?.(null, message); }; const chatToolMap = { From 75f7760cd0671714250e2002dd5367df60e1f75a Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:11:39 +0530 Subject: [PATCH 16/76] Fix logout issue (#666) * set messages and avatar url to null on logout * Fixed linting issues * Remove unnecessary import --- packages/react/src/views/ChatHeader/ChatHeader.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 0e46b9ccdd..7c43056a65 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -88,8 +88,9 @@ const ChatHeader = ({ const dispatchToastMessage = useToastBarDispatch(); const getMessagesAndRoles = useFetchChatData(showRoles); const setMessageLimit = useSettingsStore((state) => state.setMessageLimit); - + const setMessages = useMessageStore((state) => state.setMessages); const avatarUrl = useUserStore((state) => state.avatarUrl); + const setUserAvatarUrl = useUserStore((state) => state.setUserAvatarUrl); const headerTitle = useMessageStore((state) => state.headerTitle); const filtered = useMessageStore((state) => state.filtered); const setFilter = useMessageStore((state) => state.setFilter); @@ -128,6 +129,9 @@ const ChatHeader = ({ const handleLogout = useCallback(async () => { try { await RCInstance.logout(); + setMessages([]); + setUserAvatarUrl(null); + useMessageStore.setState({ isMessageLoaded: false }); } catch (e) { console.error(e); } finally { From 8ba15a5f8cc32a6c5887df8aa08374ba4599c0ba Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Fri, 29 Nov 2024 01:00:17 +0530 Subject: [PATCH 17/76] Fix: Prevent repeated API calls in searchMessages after typing stops (#655) * Fix: Prevent repeated API calls in searchMessages after typing stops * Implement Rate Limiter * Revised debounce --- .../views/MessageAggregators/SearchMessages.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/react/src/views/MessageAggregators/SearchMessages.js b/packages/react/src/views/MessageAggregators/SearchMessages.js index e627921c80..b4248461c6 100644 --- a/packages/react/src/views/MessageAggregators/SearchMessages.js +++ b/packages/react/src/views/MessageAggregators/SearchMessages.js @@ -1,4 +1,4 @@ -import React, { useState, useContext, useEffect } from 'react'; +import React, { useState, useContext, useEffect, useCallback } from 'react'; import debounce from 'lodash/debounce'; import { useComponentOverrides } from '@embeddedchat/ui-elements'; import RCContext from '../../context/RCInstance'; @@ -15,14 +15,17 @@ const SearchMessages = () => { setText(e.target.value); }; - const searchMessages = async () => { + const searchMessages = useCallback(async () => { const { messages } = await RCInstance.getSearchMessages(text); setMessageList(messages); - }; + }, [text, RCInstance]); - const debouncedSearch = debounce(async () => { - await searchMessages(); - }, 500); + const debouncedSearch = useCallback( + debounce(async () => { + await searchMessages(); + }, 500), + [searchMessages] + ); useEffect(() => { if (!text.trim()) { From 38f52a1119e4b2a0b12f60dd59f724b891d0697a Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sun, 15 Dec 2024 15:28:28 +0530 Subject: [PATCH 18/76] Fix code scanning alert no. 13: Incomplete string escaping or encoding (#684) Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/ui-elements/tools/icons-generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-elements/tools/icons-generator.js b/packages/ui-elements/tools/icons-generator.js index 7dc17fc93b..538d42f406 100644 --- a/packages/ui-elements/tools/icons-generator.js +++ b/packages/ui-elements/tools/icons-generator.js @@ -64,7 +64,7 @@ const camelCase = (name) => const codeModifier = (code) => { let newCode = code.replace(/class=/g, 'className='); const openingTag = newCode.match(//g)[0]; - const newOpeningTag = openingTag.replace('>', ' {...props}>'); + const newOpeningTag = openingTag.replace(/>/g, ' {...props}>'); newCode = newCode.replace(openingTag, newOpeningTag); return newCode; }; From 8284ffa38dca7cfe00430cc89b25f30bd7e7eece Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:31:51 +0530 Subject: [PATCH 19/76] Fix: Restrict Pin Icon Visibility Based on User Permissions (#674) * Fix:Fix: Restrict pin icon visibility based on user permissions * Restrict pin icon visibility --- packages/react/src/hooks/useRCAuth.js | 5 +++++ packages/react/src/store/userStore.js | 5 ++++- packages/react/src/views/Message/Message.js | 7 +++++++ packages/react/src/views/Message/MessageToolbox.js | 5 ++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index 83b013353b..6a997cb569 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -20,11 +20,15 @@ export const useRCAuth = () => { ); const setPassword = useUserStore((state) => state.setPassword); const setEmailorUser = useUserStore((state) => state.setEmailorUser); + const setUserPinPermissions = useUserStore( + (state) => state.setUserPinPermissions + ); const dispatchToastMessage = useToastBarDispatch(); const handleLogin = async (userOrEmail, password, code) => { try { const res = await RCInstance.login(userOrEmail, password, code); + const permissions = await RCInstance.permissionInfo(); if (res.error === 'Unauthorized' || res.error === 403) { dispatchToastMessage({ type: 'error', @@ -56,6 +60,7 @@ export const useRCAuth = () => { setIsTotpModalOpen(false); setEmailorUser(null); setPassword(null); + setUserPinPermissions(permissions.update[150]); dispatchToastMessage({ type: 'success', message: 'Successfully logged in', diff --git a/packages/react/src/store/userStore.js b/packages/react/src/store/userStore.js index c4128cf468..d3c3aa24c3 100644 --- a/packages/react/src/store/userStore.js +++ b/packages/react/src/store/userStore.js @@ -27,8 +27,11 @@ const useUserStore = create((set) => ({ setPassword: (password) => set(() => ({ password })), emailoruser: null, setEmailorUser: (emailoruser) => set(() => ({ emailoruser })), - roles: {}, + roles: [], setRoles: (roles) => set((state) => ({ ...state, roles })), + userPinPermissions: {}, + setUserPinPermissions: (userPinPermissions) => + set((state) => ({ ...state, userPinPermissions })), showCurrentUserInfo: false, setShowCurrentUserInfo: (showCurrentUserInfo) => set(() => ({ showCurrentUserInfo })), diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 8ce3d8bf5c..64d2050f09 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -51,6 +51,10 @@ const Message = ({ const authenticatedUserId = useUserStore((state) => state.userId); const authenticatedUserUsername = useUserStore((state) => state.username); + const userRoles = useUserStore((state) => state.roles); + const pinPermissions = useUserStore( + (state) => state.userPinPermissions.roles + ); const [setMessageToReport, toggleShowReportMessage] = useMessageStore( (state) => [state.setMessageToReport, state.toggleShowReportMessage] ); @@ -67,6 +71,7 @@ const Message = ({ const theme = useTheme(); const styles = getMessageStyles(theme); const bubbleStyles = useBubbleStyles(isMe); + const pinRoles = new Set(pinPermissions); const variantStyles = !isInSidebar && variantOverrides === 'bubble' ? bubbleStyles : {}; @@ -200,6 +205,8 @@ const Message = ({ message={message} isEditing={editMessage._id === message._id} authenticatedUserId={authenticatedUserId} + userRoles={userRoles} + pinRoles={pinRoles} handleOpenThread={handleOpenThread} handleDeleteMessage={handleDeleteMessage} handleStarMessage={handleStarMessage} diff --git a/packages/react/src/views/Message/MessageToolbox.js b/packages/react/src/views/Message/MessageToolbox.js index 248dd35586..55af7f64d5 100644 --- a/packages/react/src/views/Message/MessageToolbox.js +++ b/packages/react/src/views/Message/MessageToolbox.js @@ -21,6 +21,8 @@ export const MessageToolbox = ({ style = {}, isThreadMessage = false, authenticatedUserId, + userRoles, + pinRoles, handleOpenThread, handleEmojiClick, handlePinMessage, @@ -67,6 +69,7 @@ export const MessageToolbox = ({ setShowDeleteModal(false); }; + const isAllowedToPin = userRoles.some((role) => pinRoles.has(role)); const options = useMemo( () => ({ reply: { @@ -110,7 +113,7 @@ export const MessageToolbox = ({ id: 'pin', onClick: () => handlePinMessage(message), iconName: message.pinned ? 'pin-filled' : 'pin', - visible: !isThreadMessage, + visible: !isThreadMessage && isAllowedToPin, }, edit: { label: 'Edit', From 32e14bca76e10bbbf40899d6cb594eb3fccb0af2 Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:38:17 +0530 Subject: [PATCH 20/76] Made message box responsive and fixed alignment (#664) --- .../react/src/views/ChatBody/ChatBody.styles.js | 2 +- .../react/src/views/ChatInput/ChatInput.styles.js | 13 +++++++++++-- .../react/src/views/ChatLayout/ChatLayout.styles.js | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/react/src/views/ChatBody/ChatBody.styles.js b/packages/react/src/views/ChatBody/ChatBody.styles.js index 36fc549b5f..06be313a16 100644 --- a/packages/react/src/views/ChatBody/ChatBody.styles.js +++ b/packages/react/src/views/ChatBody/ChatBody.styles.js @@ -9,7 +9,7 @@ export const getChatbodyStyles = () => { overflow-x: hidden; display: flex; flex-direction: column-reverse; - max-height: 600px; + max-height: 100%; position: relative; padding-top: 70px; margin-top: 0.25rem; diff --git a/packages/react/src/views/ChatInput/ChatInput.styles.js b/packages/react/src/views/ChatInput/ChatInput.styles.js index 73e91eb971..a1474877f9 100644 --- a/packages/react/src/views/ChatInput/ChatInput.styles.js +++ b/packages/react/src/views/ChatInput/ChatInput.styles.js @@ -22,6 +22,9 @@ export const getChatInputStyles = (theme) => { justify-content: center; flex-direction: row; padding: 0.5rem; + @media (max-width: 383px) { + min-height: 100px; + } `, iconCursor: css` @@ -51,6 +54,9 @@ export const getChatInputStyles = (theme) => { &::placeholder { padding-left: 5px; } + @media (max-width: 383px) { + font-size: 18px; + } `, }; @@ -68,9 +74,12 @@ export const getChatInputFormattingToolbarStyles = ({ theme, mode }) => { : lighten(theme.colors.background, 1)}; display: flex; position: relative; - flex-direction: row; - gap: 0.375rem; + gap: 0.1rem; border-radius: 0 0 ${theme.radius} ${theme.radius}; + @media (max-width: 383px) { + display: grid; + grid-template-columns: repeat(5, 0.2fr); + } `, }; return styles; diff --git a/packages/react/src/views/ChatLayout/ChatLayout.styles.js b/packages/react/src/views/ChatLayout/ChatLayout.styles.js index 3afa726498..56dd748cbe 100644 --- a/packages/react/src/views/ChatLayout/ChatLayout.styles.js +++ b/packages/react/src/views/ChatLayout/ChatLayout.styles.js @@ -12,6 +12,7 @@ const styles = { flex: 1; flex-direction: column; position: relative; + min-width: 0; `, sidebar: css` From a6ea2bcbd4295cafe6046bd8be457aa3f1a10868 Mon Sep 17 00:00:00 2001 From: Devansh Kansagra <125076549+devanshkansagra@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:39:38 +0530 Subject: [PATCH 21/76] Fix: Quoting issues (#641) * fixed recursive quoting issue * fixed multiple quoting issue * Removed unwanted comment * added checks which causing error in deployment * Resolved the commits * Fix the issue of not able to send the quoted thread messages --- packages/react/src/store/messageStore.js | 10 +- .../src/views/AttachmentHandler/Attachment.js | 53 ++++++ .../AttachmentHandler/AttachmentMetadata.js | 2 +- .../AttachmentHandler/AudioAttachment.js | 135 ++++++++++++-- .../AttachmentHandler/ImageAttachment.js | 146 +++++++++++++-- .../views/AttachmentHandler/TextAttachment.js | 77 +++++++- .../AttachmentHandler/VideoAttachment.js | 174 +++++++++++++++--- .../react/src/views/ChatInput/ChatInput.js | 56 ++++-- .../src/views/ChatInput/ChatInput.styles.js | 4 + .../Message/BubbleVariant/Bubble.styles.js | 6 +- packages/react/src/views/Message/Message.js | 4 +- .../src/views/QuoteMessage/QuoteMessage.js | 75 +++++++- 12 files changed, 647 insertions(+), 95 deletions(-) diff --git a/packages/react/src/store/messageStore.js b/packages/react/src/store/messageStore.js index 012a97e1ed..507ba3d141 100644 --- a/packages/react/src/store/messageStore.js +++ b/packages/react/src/store/messageStore.js @@ -8,7 +8,7 @@ const useMessageStore = create((set, get) => ({ threadMessages: [], filtered: false, editMessage: {}, - quoteMessage: {}, + quoteMessage: [], messageToReport: NaN, showReportMessage: false, isRecordingMessage: false, @@ -71,7 +71,13 @@ const useMessageStore = create((set, get) => ({ } }, setEditMessage: (editMessage) => set(() => ({ editMessage })), - setQuoteMessage: (quoteMessage) => set(() => ({ quoteMessage })), + addQuoteMessage: (quoteMessage) => + set((state) => ({ quoteMessage: [...state.quoteMessage, quoteMessage] })), + removeQuoteMessage: (quoteMessage) => + set((state) => ({ + quoteMessage: state.quoteMessage.filter((i) => i !== quoteMessage), + })), + clearQuoteMessages: () => set({ quoteMessage: [] }), setMessageToReport: (messageId) => set(() => ({ messageToReport: messageId })), toggleShowReportMessage: () => { diff --git a/packages/react/src/views/AttachmentHandler/Attachment.js b/packages/react/src/views/AttachmentHandler/Attachment.js index bc07a740c1..594bfa7d70 100644 --- a/packages/react/src/views/AttachmentHandler/Attachment.js +++ b/packages/react/src/views/AttachmentHandler/Attachment.js @@ -8,11 +8,16 @@ import VideoAttachment from './VideoAttachment'; import TextAttachment from './TextAttachment'; const Attachment = ({ attachment, host, type, variantStyles = {} }) => { + const author = { + authorIcon: attachment?.author_icon, + authorName: attachment?.author_name, + }; if (attachment && attachment.audio_url) { return ( ); @@ -22,6 +27,7 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { ); @@ -31,6 +37,7 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { ); @@ -40,10 +47,56 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { ); } + if ( + attachment.attachments && + Array.isArray(attachment.attachments) && + attachment.attachments[0]?.image_url + ) { + return ( + + ); + } + if ( + attachment.attachments && + Array.isArray(attachment.attachments) && + attachment.attachments[0]?.audio_url + ) { + return ( + + ); + } + if ( + attachment.attachments && + Array.isArray(attachment.attachments) && + attachment.attachments[0]?.video_url + ) { + return ( + + ); + } return ( {

diff --git a/packages/react/src/views/AttachmentHandler/AudioAttachment.js b/packages/react/src/views/AttachmentHandler/AudioAttachment.js index ce84815828..0f5824aa0f 100644 --- a/packages/react/src/views/AttachmentHandler/AudioAttachment.js +++ b/packages/react/src/views/AttachmentHandler/AudioAttachment.js @@ -1,18 +1,129 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; -import { Box } from '@embeddedchat/ui-elements'; +import { css } from '@emotion/react'; +import { Box, Avatar, useTheme } from '@embeddedchat/ui-elements'; import AttachmentMetadata from './AttachmentMetadata'; +import RCContext from '../../context/RCInstance'; -const AudioAttachment = ({ attachment, host, variantStyles }) => ( - - - -); +const AudioAttachment = ({ attachment, host, type, author, variantStyles }) => { + const { RCInstance } = useContext(RCContext); + const { theme } = useTheme(); + const getUserAvatarUrl = (icon) => { + const instanceHost = RCInstance.getHost(); + const URL = `${instanceHost}${icon}`; + return URL; + }; + const { authorIcon, authorName } = author; + return ( + + + {type === 'file' ? ( + <> + + + @{authorName} + + + ) : ( + '' + )} + + + + ); +}; export default AudioAttachment; diff --git a/packages/react/src/views/AttachmentHandler/ImageAttachment.js b/packages/react/src/views/AttachmentHandler/ImageAttachment.js index 8937782569..b9e6533ad4 100644 --- a/packages/react/src/views/AttachmentHandler/ImageAttachment.js +++ b/packages/react/src/views/AttachmentHandler/ImageAttachment.js @@ -1,32 +1,81 @@ -import React, { useState } from 'react'; +import React, { useState, useContext } from 'react'; import { css } from '@emotion/react'; import PropTypes from 'prop-types'; -import { Box } from '@embeddedchat/ui-elements'; +import { Box, Avatar, useTheme } from '@embeddedchat/ui-elements'; import AttachmentMetadata from './AttachmentMetadata'; import ImageGallery from '../ImageGallery/ImageGallery'; +import RCContext from '../../context/RCInstance'; -const ImageAttachment = ({ attachment, host, variantStyles = {} }) => { +const ImageAttachment = ({ + attachment, + host, + type, + author, + variantStyles = {}, +}) => { + const { RCInstance } = useContext(RCContext); const [showGallery, setShowGallery] = useState(false); + const getUserAvatarUrl = (icon) => { + const instanceHost = RCInstance.getHost(); + const URL = `${instanceHost}${icon}`; + return URL; + }; const extractIdFromUrl = (url) => { const match = url.match(/\/file-upload\/(.*?)\//); return match ? match[1] : null; }; + const { theme } = useTheme(); + + const { authorIcon, authorName } = author; + return ( - setShowGallery(true)} - css={css` - cursor: pointer; - border-radius: inherit; - line-height: 0; - `} + css={[ + css` + cursor: pointer; + border-radius: inherit; + line-height: 0; + padding: 0.5rem; + `, + (type ? variantStyles.pinnedContainer : '') || + css` + ${type === 'file' + ? `border: 2px solid ${theme.colors.border};` + : ''} + `, + ]} > + {type === 'file' ? ( + <> + + + @{authorName} + + + ) : ( + '' + )} + { borderBottomRightRadius: 'inherit', }} /> + {attachment.attachments && + attachment.attachments.map((nestedAttachment, index) => ( + + setShowGallery(true)} + css={[ + css` + cursor: pointer; + border-radius: inherit; + line-height: 0; + padding: 0.5rem; + `, + (nestedAttachment.attachments[0].type + ? variantStyles.pinnedContainer + : variantStyles.quoteContainer) || + css` + ${nestedAttachment.attachments[0].type === 'file' + ? `border: 2px solid ${theme.colors.border};` + : ''} + `, + ]} + > + {nestedAttachment.type === 'file' ? ( + <> + + + @{nestedAttachment.author_name} + + + ) : ( + '' + )} + + + + {showGallery && ( + + )} + + ))} {showGallery && ( { return URL; }; - let attachmentText = attachment?.text; - if (attachmentText.includes(')')) { - attachmentText = attachmentText.split(')')[1] || ''; - } - const { theme } = useTheme(); return ( @@ -67,7 +62,77 @@ const TextAttachment = ({ attachment, type, variantStyles = {} }) => { white-space: pre-line; `} > - {attachmentText} + {attachment?.text + ? attachment.text[0] === '[' + ? attachment.text.match(/\n(.*)/)?.[1] || '' + : attachment.text + : ''} + {attachment?.attachments && + attachment.attachments.map((nestedAttachment, index) => ( + + + {nestedAttachment?.author_name && ( + <> + + @{nestedAttachment?.author_name} + + )} + + + {nestedAttachment?.text + ? nestedAttachment.text[0] === '[' + ? nestedAttachment.text.match(/\n(.*)/)?.[1] || '' + : nestedAttachment.text + : ''} + + + ))} ); diff --git a/packages/react/src/views/AttachmentHandler/VideoAttachment.js b/packages/react/src/views/AttachmentHandler/VideoAttachment.js index 0472d34c21..06e3453167 100644 --- a/packages/react/src/views/AttachmentHandler/VideoAttachment.js +++ b/packages/react/src/views/AttachmentHandler/VideoAttachment.js @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { css } from '@emotion/react'; -import { Box } from '@embeddedchat/ui-elements'; +import { Box, Avatar, useTheme } from '@embeddedchat/ui-elements'; import AttachmentMetadata from './AttachmentMetadata'; +import RCContext from '../../context/RCInstance'; const userAgentMIMETypeFallback = (type) => { const userAgent = navigator.userAgent.toLocaleLowerCase(); @@ -14,35 +15,152 @@ const userAgentMIMETypeFallback = (type) => { return type; }; -const VideoAttachment = ({ attachment, host, variantStyles = {} }) => ( - - - - + + {attachment.attachments && + attachment.attachments.map((nestedAttachment, index) => ( + + + {nestedAttachment.type === 'file' ? ( + <> + + + @{authorName} + + + ) : ( + '' + )} + + + + + ))} + - -); + ); +}; export default VideoAttachment; diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index eae9573aed..581cbf0d26 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -90,20 +90,20 @@ const ChatInput = ({ scrollToBottom }) => { editMessage, setEditMessage, quoteMessage, - setQuoteMessage, isRecordingMessage, upsertMessage, replaceMessage, + clearQuoteMessages, threadId, } = useMessageStore((state) => ({ editMessage: state.editMessage, setEditMessage: state.setEditMessage, quoteMessage: state.quoteMessage, - setQuoteMessage: state.setQuoteMessage, isRecordingMessage: state.isRecordingMessage, upsertMessage: state.upsertMessage, replaceMessage: state.replaceMessage, threadId: state.threadMainMessage?._id, + clearQuoteMessages: state.clearQuoteMessages, })); const setIsLoginModalOpen = useLoginStore( @@ -255,14 +255,31 @@ const ChatInput = ({ scrollToBottom }) => { messageRef.current.value = ''; setDisableButton(true); - const { msg, attachments, _id } = quoteMessage; let pendingMessage = ''; - - if (msg || attachments) { - setQuoteMessage({}); - const msgLink = await getMessageLink(_id); + let quotedMessages = ''; + + if (quoteMessage.length > 0) { + // for (const quote of quoteMessage) { + // const { msg, attachments, _id } = quote; + // if (msg || attachments) { + // const msgLink = await getMessageLink(_id); + // quotedMessages += `[ ](${msgLink})`; + // } + // } + + const quoteArray = await Promise.all( + quoteMessage.map(async (quote) => { + const { msg, attachments, _id } = quote; + if (msg || attachments) { + const msgLink = await getMessageLink(_id); + quotedMessages += `[ ](${msgLink})`; + } + return quotedMessages; + }) + ); + quotedMessages = quoteArray.join(''); pendingMessage = createPendingMessage( - `[ ](${msgLink})\n ${message}`, + `${quotedMessages}\n${message}`, userInfo ); } else { @@ -283,10 +300,9 @@ const ChatInput = ({ scrollToBottom }) => { ECOptions.enableThreads ? threadId : undefined ); - if (!res.success) { - handleSendError('Error sending message, login again'); - } else { - replaceMessage(pendingMessage._id, res.message); + if (res.success) { + clearQuoteMessages(); + replaceMessage(pendingMessage, res.message); } }; @@ -425,14 +441,14 @@ const ChatInput = ({ scrollToBottom }) => { return ( - - {(quoteMessage.msg || quoteMessage.attachments) && ( - - )} + +

+ {quoteMessage && + quoteMessage.length > 0 && + quoteMessage.map((message, index) => ( + + ))} +
{editMessage.msg || editMessage.attachments || isChannelReadOnly ? ( { font-size: 18px; } `, + quoteContainer: css` + max-height: 300px; + overflow: scroll; + `, }; return styles; diff --git a/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js b/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js index 42f978f9eb..5f87c0b5e4 100644 --- a/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js +++ b/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js @@ -94,7 +94,7 @@ export const getBubbleStyles = (theme) => { overflow: hidden; `, pinnedContainer: css` - max-width: 80%; + max-width: 100%; `, quoteContainer: css` @@ -112,7 +112,7 @@ export const getBubbleStyles = (theme) => { `, attachmentMetaContainer: css` - padding: 2.5% 2.5% 0; + padding: 2.5% 0 0; `, emojiPickerStyles: css` @@ -172,7 +172,7 @@ export const getBubbleStylesMe = (theme) => { pinnedContainerMe: css` border-inline-start: none; - border-inline-end: 3px solid ${theme.colors.border}; + border-inline-end: none; `, textUserInfoMe: css` diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 64d2050f09..7f7fde3752 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -58,7 +58,7 @@ const Message = ({ const [setMessageToReport, toggleShowReportMessage] = useMessageStore( (state) => [state.setMessageToReport, state.toggleShowReportMessage] ); - const setQuoteMessage = useMessageStore((state) => state.setQuoteMessage); + const addQuoteMessage = useMessageStore((state) => state.addQuoteMessage); const openThread = useMessageStore((state) => state.openThread); const dispatchToastMessage = useToastBarDispatch(); @@ -218,7 +218,7 @@ const Message = ({ setEditMessage(message); } }} - handleQuoteMessage={() => setQuoteMessage(message)} + handleQuoteMessage={() => addQuoteMessage(message)} handleEmojiClick={handleEmojiClick} handlerReportMessage={() => { setMessageToReport(message._id); diff --git a/packages/react/src/views/QuoteMessage/QuoteMessage.js b/packages/react/src/views/QuoteMessage/QuoteMessage.js index fab425ebaa..be715cf0ed 100644 --- a/packages/react/src/views/QuoteMessage/QuoteMessage.js +++ b/packages/react/src/views/QuoteMessage/QuoteMessage.js @@ -11,17 +11,21 @@ import { import RCContext from '../../context/RCInstance'; import { useMessageStore } from '../../store'; import getQuoteMessageStyles from './QuoteMessage.styles'; +import Attachment from '../AttachmentHandler/Attachment'; const QuoteMessage = ({ className = '', style = {}, message }) => { const { RCInstance } = useContext(RCContext); + const instanceHost = RCInstance.getHost(); const getUserAvatarUrl = (username) => { - const host = RCInstance.getHost(); + const host = instanceHost; const URL = `${host}/avatar/${username}`; return URL; }; const { theme } = useTheme(); const styles = getQuoteMessageStyles(theme); - const setQuoteMessage = useMessageStore((state) => state.setQuoteMessage); + const removeQuoteMessage = useMessageStore( + (state) => state.removeQuoteMessage + ); const { classNames, styleOverrides } = useComponentOverrides('QuoteMessage'); return ( @@ -31,7 +35,11 @@ const QuoteMessage = ({ className = '', style = {}, message }) => { css={styles.messageContainer} > - setQuoteMessage({})} size="small"> + removeQuoteMessage(message)} + size="small" + > @@ -45,11 +53,62 @@ const QuoteMessage = ({ className = '', style = {}, message }) => { {format(new Date(message.ts), 'h:mm a')}
- {message.msg - ? message.msg - : `${message.file?.name} (${ - message.file?.size ? (message.file.size / 1024).toFixed(2) : 0 - } kB)`} + {message.file ? ( + message.file.type.startsWith('image/') ? ( +
+ {message.file.name} +
{`${message.file.name} (${(message.file.size / 1024).toFixed( + 2 + )} kB)`}
+
+ ) : message.file.type.startsWith('video/') ? ( + + ) : message.file.type.startsWith('audio/') ? ( + + ) : ( + + {message.msg + ? message.msg + : `${message.file?.name} (${ + message.file?.size + ? (message.file.size / 1024).toFixed(2) + : 0 + } kB)`} + + ) + ) : message?.msg[0] === '[' ? ( + message?.msg.match(/\n(.*)/)[1] + ) : ( + message?.msg + )} + {message.attachments && + message.attachments.length > 0 && + message.msg && + message.msg[0] === '[' && + message.attachments.map((attachment, index) => ( + + ))}
); From 28a82023953ea9316b1687574dcbe97647a992ed Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:45:08 +0530 Subject: [PATCH 22/76] Feat: Added Scroll-to-Message Functionality (#667) * Added go to Message Feature * fix linting issues * Implement jump to message functionality * Fix linting * Append msg to URL * Remove appending in URL * Fix popup mode issue --- .../common/MessageAggregator.js | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 079582e86a..58fccff3c4 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -1,10 +1,17 @@ import React, { useState, useMemo } from 'react'; import { isSameDay, format } from 'date-fns'; -import { Box, Sidebar, Popup, useTheme } from '@embeddedchat/ui-elements'; +import { + Box, + Sidebar, + Popup, + useTheme, + ActionButton, + Icon, +} from '@embeddedchat/ui-elements'; import { MessageDivider } from '../../Message/MessageDivider'; import Message from '../../Message/Message'; import getMessageAggregatorStyles from './MessageAggregator.styles'; -import { useMessageStore } from '../../../store'; +import { useMessageStore, useSidebarStore } from '../../../store'; import { useSetMessageList } from '../../../hooks/useSetMessageList'; import LoadingIndicator from './LoadingIndicator'; import NoMessagesIndicator from './NoMessageIndicator'; @@ -37,6 +44,17 @@ export const MessageAggregator = ({ shouldRender ); + const setShowSidebar = useSidebarStore((state) => state.setShowSidebar); + const setJumpToMessage = (msgId) => { + if (msgId) { + const element = document.getElementById(`ec-message-body-${msgId}`); + if (element) { + setShowSidebar(false); + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } + }; + const isMessageNewDay = (current, previous) => !previous || !shouldRender(previous) || @@ -90,20 +108,38 @@ export const MessageAggregator = ({ fileMessage={msg} /> ) : ( - + > + + + setJumpToMessage(msg._id)} + css={{ + position: 'relative', + zIndex: 10, + }} + > + + +
)} ); From f64a3a6525436636abf75d1cffc4463096d36ac6 Mon Sep 17 00:00:00 2001 From: Rahul Singh Thakur <65606499+Barrylimarti@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:48:45 +0530 Subject: [PATCH 23/76] Fix: Sidebar starred messages fetched by api to include thread messages. (#669) * fetch starred message api called * starred messages fetching from api * ran prettier * fixed linting errors --- packages/react/src/hooks/useFetchChatData.js | 23 +++++++++++++- .../react/src/store/starredMessageStore.js | 2 ++ packages/react/src/views/ChatBody/ChatBody.js | 2 +- .../react/src/views/ChatHeader/ChatHeader.js | 2 +- .../react/src/views/ChatLayout/ChatLayout.js | 30 +++++++++++++++++-- packages/react/src/views/Message/Message.js | 4 ++- .../MessageAggregators/StarredMessages.js | 8 +++-- .../common/MessageAggregator.js | 5 ++-- 8 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/react/src/hooks/useFetchChatData.js b/packages/react/src/hooks/useFetchChatData.js index 1d08f0eaf4..99c48aa1e4 100644 --- a/packages/react/src/hooks/useFetchChatData.js +++ b/packages/react/src/hooks/useFetchChatData.js @@ -5,6 +5,7 @@ import { useChannelStore, useMemberStore, useMessageStore, + useStarredMessageStore, } from '../store'; const useFetchChatData = (showRoles) => { @@ -13,6 +14,9 @@ const useFetchChatData = (showRoles) => { const isChannelPrivate = useChannelStore((state) => state.isChannelPrivate); const setMessages = useMessageStore((state) => state.setMessages); const setAdmins = useMemberStore((state) => state.setAdmins); + const setStarredMessages = useStarredMessageStore( + (state) => state.setStarredMessages + ); const isUserAuthenticated = useUserStore( (state) => state.isUserAuthenticated ); @@ -80,7 +84,24 @@ const useFetchChatData = (showRoles) => { ] ); - return getMessagesAndRoles; + const getStarredMessages = useCallback( + async (anonymousMode) => { + if (isUserAuthenticated) { + try { + if (!isUserAuthenticated && !anonymousMode) { + return; + } + const { messages } = await RCInstance.getStarredMessages(); + setStarredMessages(messages); + } catch (e) { + console.error(e); + } + } + }, + [isUserAuthenticated, RCInstance, setStarredMessages] + ); + + return { getMessagesAndRoles, getStarredMessages }; }; export default useFetchChatData; diff --git a/packages/react/src/store/starredMessageStore.js b/packages/react/src/store/starredMessageStore.js index a564df3c41..989ec8b6fb 100644 --- a/packages/react/src/store/starredMessageStore.js +++ b/packages/react/src/store/starredMessageStore.js @@ -3,6 +3,8 @@ import { create } from 'zustand'; const useStarredMessageStore = create((set) => ({ showStarred: false, setShowStarred: (showStarred) => set(() => ({ showStarred })), + starredMessages: [], + setStarredMessages: (messages) => set(() => ({ starredMessages: messages })), })); export default useStarredMessageStore; diff --git a/packages/react/src/views/ChatBody/ChatBody.js b/packages/react/src/views/ChatBody/ChatBody.js index e5a6bd1a33..a582244d0f 100644 --- a/packages/react/src/views/ChatBody/ChatBody.js +++ b/packages/react/src/views/ChatBody/ChatBody.js @@ -69,7 +69,7 @@ const ChatBody = ({ const username = useUserStore((state) => state.username); - const getMessagesAndRoles = useFetchChatData(showRoles); + const { getMessagesAndRoles } = useFetchChatData(showRoles); const getThreadMessages = useCallback(async () => { if (isUserAuthenticated && threadMainMessage?._id) { diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 7c43056a65..99babdc51a 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -86,7 +86,7 @@ const ChatHeader = ({ ); const dispatchToastMessage = useToastBarDispatch(); - const getMessagesAndRoles = useFetchChatData(showRoles); + const { getMessagesAndRoles } = useFetchChatData(showRoles); const setMessageLimit = useSettingsStore((state) => state.setMessageLimit); const setMessages = useMessageStore((state) => state.setMessages); const avatarUrl = useUserStore((state) => state.avatarUrl); diff --git a/packages/react/src/views/ChatLayout/ChatLayout.js b/packages/react/src/views/ChatLayout/ChatLayout.js index 6243dc0cc8..ee6b0c1b94 100644 --- a/packages/react/src/views/ChatLayout/ChatLayout.js +++ b/packages/react/src/views/ChatLayout/ChatLayout.js @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useRef, useCallback, useState } from 'react'; import { Box, useComponentOverrides } from '@embeddedchat/ui-elements'; import styles from './ChatLayout.styles'; import { @@ -36,9 +36,15 @@ import useUiKitStore from '../../store/uiKitStore'; const ChatLayout = () => { const messageListRef = useRef(null); const { classNames, styleOverrides } = useComponentOverrides('ChatBody'); - const { ECOptions } = useRCContext(); + const { RCInstance, ECOptions } = useRCContext(); const anonymousMode = ECOptions?.anonymousMode; const showRoles = ECOptions?.anonymousMode; + const setStarredMessages = useStarredMessageStore( + (state) => state.setStarredMessages + ); + const starredMessages = useStarredMessageStore( + (state) => state.starredMessages + ); const showSidebar = useSidebarStore((state) => state.showSidebar); const showMentions = useMentionsStore((state) => state.showMentions); const showAllFiles = useFileStore((state) => state.showAllFiles); @@ -57,6 +63,9 @@ const ChatLayout = () => { const attachmentWindowOpen = useAttachmentWindowStore( (state) => state.attachmentWindowOpen ); + const isUserAuthenticated = useUserStore( + (state) => state.isUserAuthenticated + ); const { data, handleDrag, handleDragDrop } = useDropBox(); const { uiKitContextualBarOpen, uiKitContextualBarData } = useUiKitStore( (state) => ({ @@ -72,7 +81,22 @@ const ChatLayout = () => { }); } }; - + const getStarredMessages = useCallback(async () => { + if (isUserAuthenticated) { + try { + if (!isUserAuthenticated && !anonymousMode) { + return; + } + const { messages } = await RCInstance.getStarredMessages(); + setStarredMessages(messages); + } catch (e) { + console.error(e); + } + } + }, [isUserAuthenticated, anonymousMode, RCInstance]); + useEffect(() => { + getStarredMessages(); + }, [showSidebar]); return ( state.addQuoteMessage); const openThread = useMessageStore((state) => state.openThread); - + const { getStarredMessages } = useFetchChatData(); const dispatchToastMessage = useToastBarDispatch(); const { editMessage, setEditMessage } = useMessageStore((state) => ({ editMessage: state.editMessage, @@ -92,6 +93,7 @@ const Message = ({ message: 'Message unstarred', }); } + getStarredMessages(); }; const handlePinMessage = async (msg) => { diff --git a/packages/react/src/views/MessageAggregators/StarredMessages.js b/packages/react/src/views/MessageAggregators/StarredMessages.js index b7d77b864e..5ced944f06 100644 --- a/packages/react/src/views/MessageAggregators/StarredMessages.js +++ b/packages/react/src/views/MessageAggregators/StarredMessages.js @@ -1,12 +1,15 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { useComponentOverrides } from '@embeddedchat/ui-elements'; -import { useUserStore } from '../../store'; +import { useStarredMessageStore, useUserStore } from '../../store'; import { MessageAggregator } from './common/MessageAggregator'; const StarredMessages = () => { const authenticatedUserId = useUserStore((state) => state.userId); const { variantOverrides } = useComponentOverrides('StarredMessages'); const viewType = variantOverrides.viewType || 'Sidebar'; + const starredMessages = useStarredMessageStore( + (state) => state.starredMessages + ); const shouldRender = useCallback( (msg) => msg.starred && @@ -18,6 +21,7 @@ const StarredMessages = () => { title="Starred Messages" iconName="star" noMessageInfo="No Starred Messages" + fetchedMessageList={starredMessages} shouldRender={shouldRender} viewType={viewType} /> diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 58fccff3c4..3f4f62e730 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -23,6 +23,7 @@ export const MessageAggregator = ({ iconName, noMessageInfo, shouldRender, + fetchedMessageList, searchProps, searchFiltered, fetching, @@ -40,7 +41,7 @@ export const MessageAggregator = ({ ); const [messageRendered, setMessageRendered] = useState(false); const { loading, messageList } = useSetMessageList( - searchFiltered || allMessages, + fetchedMessageList || searchFiltered || allMessages, shouldRender ); @@ -57,7 +58,7 @@ export const MessageAggregator = ({ const isMessageNewDay = (current, previous) => !previous || - !shouldRender(previous) || + shouldRender(previous) || !isSameDay(new Date(current.ts), new Date(previous.ts)); const noMessages = messageList?.length === 0 || !messageRendered; From 10584052690572433dfae0103229189379e8b7af Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:27:43 +0530 Subject: [PATCH 24/76] fix: ensure pin permission changes reflect without requiring user logout (#688) --- packages/react/src/views/EmbeddedChat.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index 2dc8379879..a7e258b442 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -83,6 +83,9 @@ const EmbeddedChat = (props) => { })); const setIsLoginIn = useLoginStore((state) => state.setIsLoginIn); + const setUserPinPermissions = useUserStore( + (state) => state.setUserPinPermissions + ); if (isClosable && !setClosableState) { throw Error( @@ -125,6 +128,8 @@ const EmbeddedChat = (props) => { setIsLoginIn(true); try { await RCInstance.autoLogin(auth); + const permissions = await RCInstance.permissionInfo(); + setUserPinPermissions(permissions.update[150]); } catch (error) { console.error(error); } finally { From 9a3b7ac93c3a2233ab7ceb2d49d205317cde8b16 Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sun, 22 Dec 2024 13:57:01 +0530 Subject: [PATCH 25/76] Fixed UI issues for Sidebar (#634) * Fixed UI issues for Sidebar * Fixed sidebar height issue * Removed unnecessary spacing * Fixed reply in thread issue * Adjusted dynamic header visibility * Revert zIndex changes * Linting fix * Run prettier * Remove zIndex * Adjusted zIndex for ViewComponent * Fix sidebar width * Remove global css for sidebar --------- Co-authored-by: Zishan Ahmad --- packages/react/src/views/ChatHeader/ChatHeader.js | 4 +++- packages/react/src/views/GlobalStyles.js | 12 +++++++++++- packages/react/src/views/Message/Message.js | 5 +++-- packages/react/src/views/Message/Message.styles.js | 3 +++ .../MessageAggregators/common/MessageAggregator.js | 9 ++++++++- .../src/views/RoomInformation/RoomInformation.js | 1 + packages/react/src/views/RoomMembers/RoomMember.js | 1 + 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 99babdc51a..0107399771 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -21,6 +21,7 @@ import { usePinnedMessageStore, useStarredMessageStore, useFileStore, + useSidebarStore, } from '../../store'; import { DynamicHeader } from '../DynamicHeader'; import useFetchChatData from '../../hooks/useFetchChatData'; @@ -84,7 +85,7 @@ const ChatHeader = ({ const setIsUserAuthenticated = useUserStore( (state) => state.setIsUserAuthenticated ); - + const setShowSidebar = useSidebarStore((state) => state.setShowSidebar); const dispatchToastMessage = useToastBarDispatch(); const { getMessagesAndRoles } = useFetchChatData(showRoles); const setMessageLimit = useSettingsStore((state) => state.setMessageLimit); @@ -130,6 +131,7 @@ const ChatHeader = ({ try { await RCInstance.logout(); setMessages([]); + setShowSidebar(false); setUserAvatarUrl(null); useMessageStore.setState({ isMessageLoaded: false }); } catch (e) { diff --git a/packages/react/src/views/GlobalStyles.js b/packages/react/src/views/GlobalStyles.js index b26977e635..c9c821feff 100644 --- a/packages/react/src/views/GlobalStyles.js +++ b/packages/react/src/views/GlobalStyles.js @@ -8,7 +8,6 @@ const getGlobalStyles = (theme) => css` margin: 0; padding: 0; } - .ec-embedded-chat body { font-family: ${theme.typography.default.fontFamily}; font-size: ${theme.typography.default.fontSize}px; @@ -36,6 +35,17 @@ const getGlobalStyles = (theme) => css` .ec-embedded-chat ::-webkit-scrollbar-button { display: none; } + @media (max-width: 780px) { + .ec-sidebar { + position: absolute; + width: 100% !important; + height: calc(100% - 56.39px) !important; + min-width: 250px !important; + left: 0; + bottom: 0; + background: ${theme.colors.background}!important; + } + } `; const GlobalStyles = () => { diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index e460adad0d..b587d09e11 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -11,7 +11,7 @@ import { import { Attachments } from '../AttachmentHandler'; import { Markdown } from '../Markdown'; import MessageHeader from './MessageHeader'; -import { useMessageStore, useUserStore } from '../../store'; +import { useMessageStore, useUserStore, useSidebarStore } from '../../store'; import RCContext from '../../context/RCInstance'; import { MessageBody } from './MessageBody'; import { MessageReactions } from './MessageReactions'; @@ -49,7 +49,7 @@ const Message = ({ const { RCInstance, ECOptions } = useContext(RCContext); showAvatar = ECOptions?.showAvatar && showAvatar; - + const { showSidebar, setShowSidebar } = useSidebarStore(); const authenticatedUserId = useUserStore((state) => state.userId); const authenticatedUserUsername = useUserStore((state) => state.username); const userRoles = useUserStore((state) => state.roles); @@ -137,6 +137,7 @@ const Message = ({ const handleOpenThread = (msg) => async () => { openThread(msg); + setShowSidebar(false); }; const isStarred = message.starred?.find((u) => u._id === authenticatedUserId); diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index b6b978fb4c..9f9358fd92 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -81,6 +81,9 @@ export const getMessageDividerStyles = (theme) => { margin-bottom: 0.75rem; padding-left: 1.25rem; padding-right: 1.25rem; + @media (max-width: 780px) { + z-index: 1; + } `, dividerContent: css` diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 3f4f62e730..6e1786a339 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -70,7 +70,11 @@ export const MessageAggregator = ({ iconName={iconName} searchProps={searchProps} onClose={() => setExclusiveState(null)} - style={{ padding: 0 }} + style={{ + width: '400px', + padding: 0, + zIndex: window.innerWidth <= 780 ? 1 : null, + }} {...(viewType === 'Popup' ? { isPopupHeader: true, @@ -126,6 +130,9 @@ export const MessageAggregator = ({ isInSidebar style={{ flex: 1, + paddingLeft: 3, + paddingRight: 2, + minWidth: 0, }} /> diff --git a/packages/react/src/views/RoomInformation/RoomInformation.js b/packages/react/src/views/RoomInformation/RoomInformation.js index 504b0f4ba8..3cfdefad08 100644 --- a/packages/react/src/views/RoomInformation/RoomInformation.js +++ b/packages/react/src/views/RoomInformation/RoomInformation.js @@ -30,6 +30,7 @@ const Roominfo = () => { title="Room Information" iconName="info" onClose={() => setExclusiveState(null)} + style={{ width: '400px', zIndex: window.innerWidth <= 780 ? 1 : null }} {...(viewType === 'Popup' ? { isPopupHeader: true, diff --git a/packages/react/src/views/RoomMembers/RoomMember.js b/packages/react/src/views/RoomMembers/RoomMember.js index c52f48c9dd..85b268b07a 100644 --- a/packages/react/src/views/RoomMembers/RoomMember.js +++ b/packages/react/src/views/RoomMembers/RoomMember.js @@ -55,6 +55,7 @@ const RoomMembers = ({ members }) => { title="Members" iconName="members" onClose={() => setExclusiveState(null)} + style={{ width: '400px', zIndex: window.innerWidth <= 780 ? 1 : null }} {...(viewType === 'Popup' ? { isPopupHeader: true, From 3d6700c2f93cc9bb9eb4bd54fdfec4009befca6b Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:21:20 +0530 Subject: [PATCH 26/76] fix(permissions): ensure admin-granted edit message permissions apply in EmbeddedChat (#702) --- packages/react/src/hooks/useRCAuth.js | 11 ++++++++++- packages/react/src/store/messageStore.js | 3 +++ packages/react/src/views/EmbeddedChat.js | 6 +++++- packages/react/src/views/Message/Message.js | 5 +++++ packages/react/src/views/Message/MessageToolbox.js | 8 +++++++- 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index 6a997cb569..c0b1d3a3b4 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -1,7 +1,12 @@ import { useContext } from 'react'; import { useToastBarDispatch } from '@embeddedchat/ui-elements'; import RCContext from '../context/RCInstance'; -import { useUserStore, totpModalStore, useLoginStore } from '../store'; +import { + useUserStore, + totpModalStore, + useLoginStore, + useMessageStore, +} from '../store'; export const useRCAuth = () => { const { RCInstance } = useContext(RCContext); @@ -23,6 +28,9 @@ export const useRCAuth = () => { const setUserPinPermissions = useUserStore( (state) => state.setUserPinPermissions ); + const setEditMessagePermissions = useMessageStore( + (state) => state.setEditMessagePermissions + ); const dispatchToastMessage = useToastBarDispatch(); const handleLogin = async (userOrEmail, password, code) => { @@ -61,6 +69,7 @@ export const useRCAuth = () => { setEmailorUser(null); setPassword(null); setUserPinPermissions(permissions.update[150]); + setEditMessagePermissions(permissions.update[28]); dispatchToastMessage({ type: 'success', message: 'Successfully logged in', diff --git a/packages/react/src/store/messageStore.js b/packages/react/src/store/messageStore.js index 507ba3d141..676258c1c4 100644 --- a/packages/react/src/store/messageStore.js +++ b/packages/react/src/store/messageStore.js @@ -71,6 +71,9 @@ const useMessageStore = create((set, get) => ({ } }, setEditMessage: (editMessage) => set(() => ({ editMessage })), + editMessagePermissions: {}, + setEditMessagePermissions: (editMessagePermissions) => + set((state) => ({ ...state, editMessagePermissions })), addQuoteMessage: (quoteMessage) => set((state) => ({ quoteMessage: [...state.quoteMessage, quoteMessage] })), removeQuoteMessage: (quoteMessage) => diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index a7e258b442..00557f289c 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -18,7 +18,7 @@ import { import { ChatLayout } from './ChatLayout'; import { ChatHeader } from './ChatHeader'; import { RCInstanceProvider } from '../context/RCInstance'; -import { useUserStore, useLoginStore } from '../store'; +import { useUserStore, useLoginStore, useMessageStore } from '../store'; import DefaultTheme from '../theme/DefaultTheme'; import { getTokenStorage } from '../lib/auth'; import { styles } from './EmbeddedChat.styles'; @@ -87,6 +87,9 @@ const EmbeddedChat = (props) => { (state) => state.setUserPinPermissions ); + const setEditMessagePermissions = useMessageStore( + (state) => state.setEditMessagePermissions + ); if (isClosable && !setClosableState) { throw Error( 'Please provide a setClosableState to props when isClosable = true' @@ -130,6 +133,7 @@ const EmbeddedChat = (props) => { await RCInstance.autoLogin(auth); const permissions = await RCInstance.permissionInfo(); setUserPinPermissions(permissions.update[150]); + setEditMessagePermissions(permissions.update[28]); } catch (error) { console.error(error); } finally { diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index b587d09e11..aafb5642b5 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -56,6 +56,9 @@ const Message = ({ const pinPermissions = useUserStore( (state) => state.userPinPermissions.roles ); + const editMessagePermissions = useMessageStore( + (state) => state.editMessagePermissions.roles + ); const [setMessageToReport, toggleShowReportMessage] = useMessageStore( (state) => [state.setMessageToReport, state.toggleShowReportMessage] ); @@ -73,6 +76,7 @@ const Message = ({ const styles = getMessageStyles(theme); const bubbleStyles = useBubbleStyles(isMe); const pinRoles = new Set(pinPermissions); + const editMessageRoles = new Set(editMessagePermissions); const variantStyles = !isInSidebar && variantOverrides === 'bubble' ? bubbleStyles : {}; @@ -210,6 +214,7 @@ const Message = ({ authenticatedUserId={authenticatedUserId} userRoles={userRoles} pinRoles={pinRoles} + editMessageRoles={editMessageRoles} handleOpenThread={handleOpenThread} handleDeleteMessage={handleDeleteMessage} handleStarMessage={handleStarMessage} diff --git a/packages/react/src/views/Message/MessageToolbox.js b/packages/react/src/views/Message/MessageToolbox.js index 55af7f64d5..61765c8dd9 100644 --- a/packages/react/src/views/Message/MessageToolbox.js +++ b/packages/react/src/views/Message/MessageToolbox.js @@ -23,6 +23,7 @@ export const MessageToolbox = ({ authenticatedUserId, userRoles, pinRoles, + editMessageRoles, handleOpenThread, handleEmojiClick, handlePinMessage, @@ -70,6 +71,11 @@ export const MessageToolbox = ({ }; const isAllowedToPin = userRoles.some((role) => pinRoles.has(role)); + const isAllowedToEditMessage = userRoles.some((role) => + editMessageRoles.has(role) + ) + ? true + : message.u._id === authenticatedUserId; const options = useMemo( () => ({ reply: { @@ -120,7 +126,7 @@ export const MessageToolbox = ({ id: 'edit', onClick: () => handleEditMessage(message), iconName: 'edit', - visible: message.u._id === authenticatedUserId, + visible: isAllowedToEditMessage, color: isEditing ? 'secondary' : 'default', ghost: !isEditing, }, From 0d7ff8d496326394a21191127b1f8579b209ec54 Mon Sep 17 00:00:00 2001 From: Smriti Doneria Date: Sun, 22 Dec 2024 14:38:04 +0530 Subject: [PATCH 27/76] fixed all markdown issues (#614) * fix mardown * fix conflicts * fix formatting issues * fixed lint issues * changes * prettier * fix quote * fixed * removed markdown from passed prop --------- Co-authored-by: Zishan Ahmad --- packages/react/src/views/ChatInput/ChatInput.js | 1 + .../react/src/views/Message/MessageToolbox.js | 4 ++-- .../src/views/QuoteMessage/QuoteMessage.js | 17 +++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 581cbf0d26..86b4b5082a 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -34,6 +34,7 @@ import useShowCommands from '../../hooks/useShowCommands'; import useSearchMentionUser from '../../hooks/useSearchMentionUser'; import formatSelection from '../../lib/formatSelection'; import { parseEmoji } from '../../lib/emoji'; +import { Markdown } from '../Markdown'; const ChatInput = ({ scrollToBottom }) => { const { styleOverrides, classNames } = useComponentOverrides('ChatInput'); diff --git a/packages/react/src/views/Message/MessageToolbox.js b/packages/react/src/views/Message/MessageToolbox.js index 61765c8dd9..0ed468bd7a 100644 --- a/packages/react/src/views/Message/MessageToolbox.js +++ b/packages/react/src/views/Message/MessageToolbox.js @@ -10,9 +10,9 @@ import { useTheme, } from '@embeddedchat/ui-elements'; import { EmojiPicker } from '../EmojiPicker'; -import { parseEmoji } from '../../lib/emoji'; import { getMessageToolboxStyles } from './Message.styles'; import SurfaceMenu from '../SurfaceMenu/SurfaceMenu'; +import { Markdown } from '../Markdown'; export const MessageToolbox = ({ className = '', @@ -249,7 +249,7 @@ export const MessageToolbox = ({ padding: '0 0.5rem 0.5rem', }} > - {parseEmoji(message.msg)} + )} + + setSearchTerm(e.target.value)} + placeholder="Search members" + /> + + + + {filteredMembers.length > 0 ? ( + filteredMembers.map((member) => ( + <> + + + )) + ) : ( + No members found + )} + )} @@ -91,6 +126,7 @@ const RoomMembers = ({ members }) => { ); }; + export default RoomMembers; RoomMembers.propTypes = { diff --git a/packages/react/src/views/RoomMembers/RoomMemberItem.js b/packages/react/src/views/RoomMembers/RoomMemberItem.js index 336dc7ae95..0c38355eb9 100644 --- a/packages/react/src/views/RoomMembers/RoomMemberItem.js +++ b/packages/react/src/views/RoomMembers/RoomMemberItem.js @@ -4,6 +4,8 @@ import { css } from '@emotion/react'; import { Box, Icon, Avatar } from '@embeddedchat/ui-elements'; import RCContext from '../../context/RCInstance'; import { RoomMemberItemStyles as styles } from './RoomMembers.styles'; +import useSetExclusiveState from '../../hooks/useSetExclusiveState'; +import { useUserStore } from '../../store'; const RoomMemberItem = ({ user, host }) => { const { RCInstance } = useContext(RCContext); @@ -18,15 +20,28 @@ const RoomMemberItem = ({ user, host }) => { setUserStatus(res.status); } } catch (err) { - console.error('Error fetching user status', err); + console.error('Error fetching user status:', err); } }; getStatus(); }, [RCInstance]); + const setExclusiveState = useSetExclusiveState(); + const { setShowCurrentUserInfo, setCurrentUser } = useUserStore((state) => ({ + setShowCurrentUserInfo: state.setShowCurrentUserInfo, + setCurrentUser: state.setCurrentUser, + })); + const handleShowUserInfo = () => { + setExclusiveState(setShowCurrentUserInfo); + setCurrentUser(user); + }; return ( - + { {userStatus && ( )} - {user.username} + + {user.name} ({user.username}) + ); diff --git a/packages/react/src/views/RoomMembers/RoomMembers.styles.js b/packages/react/src/views/RoomMembers/RoomMembers.styles.js index 68d3307f5a..ac9df00bf0 100644 --- a/packages/react/src/views/RoomMembers/RoomMembers.styles.js +++ b/packages/react/src/views/RoomMembers/RoomMembers.styles.js @@ -1,14 +1,48 @@ import { css } from '@emotion/react'; -export const getRoomMemberStyles = () => { +export const getRoomMemberStyles = (theme) => { const styles = { container: css` display: flex; flex-direction: column; - overflow: auto; + height: 100%; width: 100%; - justify-content: center; padding: 0 1rem 1rem; + box-sizing: border-box; + `, + searchContainer: css` + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid ${theme.colors.border}; + padding: 0 0.5rem; + border-radius: ${theme.radius}; + position: relative; + margin-top: 1rem; + `, + textInput: css` + flex: 1; + border: none; + padding: none; + font-size: 1rem; + &:focus { + outline: none; + } + `, + searchIcon: css` + padding-left: 0.5rem; + font-size: 1.25rem; + color: ${theme.colors.icon}; + `, + memberList: css` + flex: 1; + overflow-y: auto; + margin-top: 1rem; + `, + noMembers: css` + text-align: center; + color: ${theme.colors.textSecondary}; + margin-top: 1rem; `, }; diff --git a/packages/react/src/views/UserInformation/UserInformation.js b/packages/react/src/views/UserInformation/UserInformation.js index 8d92026777..78c93ee02e 100644 --- a/packages/react/src/views/UserInformation/UserInformation.js +++ b/packages/react/src/views/UserInformation/UserInformation.js @@ -59,6 +59,10 @@ const UserInformation = () => { title="User Info" iconName="user" onClose={() => setExclusiveState(null)} + style={{ + width: '400px', + zIndex: window.innerWidth <= 780 ? 1 : null, + }} {...(viewType === 'Popup' ? { isPopupHeader: true, From 21d6f2dd4f97bd731e28222ce9aba8ebeabd90e7 Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:32:05 +0530 Subject: [PATCH 34/76] fix: display bio, nickname, and statusText in User Info modal; resolve permission issues for viewing full user info (#756) --- packages/react/src/hooks/useFetchChatData.js | 6 +++ packages/react/src/store/userStore.js | 3 ++ .../views/UserInformation/UserInformation.js | 49 ++++++++++++++++--- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/packages/react/src/hooks/useFetchChatData.js b/packages/react/src/hooks/useFetchChatData.js index 99c48aa1e4..9eb04e9bee 100644 --- a/packages/react/src/hooks/useFetchChatData.js +++ b/packages/react/src/hooks/useFetchChatData.js @@ -20,6 +20,9 @@ const useFetchChatData = (showRoles) => { const isUserAuthenticated = useUserStore( (state) => state.isUserAuthenticated ); + const setViewUserInfoPermissions = useUserStore( + (state) => state.setViewUserInfoPermissions + ); const getMessagesAndRoles = useCallback( async (anonymousMode) => { @@ -68,6 +71,9 @@ const useFetchChatData = (showRoles) => { setMemberRoles(rolesObj); } + + const permissions = await RCInstance.permissionInfo(); + setViewUserInfoPermissions(permissions.update[70]); } catch (e) { console.error(e); } diff --git a/packages/react/src/store/userStore.js b/packages/react/src/store/userStore.js index d3c3aa24c3..a19fef4714 100644 --- a/packages/react/src/store/userStore.js +++ b/packages/react/src/store/userStore.js @@ -32,6 +32,9 @@ const useUserStore = create((set) => ({ userPinPermissions: {}, setUserPinPermissions: (userPinPermissions) => set((state) => ({ ...state, userPinPermissions })), + viewUserInfoPermissions: {}, + setViewUserInfoPermissions: (viewUserInfoPermissions) => + set((state) => ({ ...state, viewUserInfoPermissions })), showCurrentUserInfo: false, setShowCurrentUserInfo: (showCurrentUserInfo) => set(() => ({ showCurrentUserInfo })), diff --git a/packages/react/src/views/UserInformation/UserInformation.js b/packages/react/src/views/UserInformation/UserInformation.js index 78c93ee02e..098608c84f 100644 --- a/packages/react/src/views/UserInformation/UserInformation.js +++ b/packages/react/src/views/UserInformation/UserInformation.js @@ -28,9 +28,15 @@ const UserInformation = () => { const [currentUserInfo, setCurrentUserInfo] = useState({}); const [isUserInfoFetched, setIsUserInfoFetched] = useState(false); const currentUser = useUserStore((state) => state.currentUser); - const authenticatedUserRoles = useUserStore((state) => state.roles); + const currentUserRoles = useUserStore((state) => state.roles); + const viewUserFullInfoRoles = useUserStore( + (state) => state.viewUserInfoPermissions.roles + ); const authenticatedUserId = useUserStore((state) => state.userId); - const isAdmin = authenticatedUserRoles?.includes('admin'); + const viewInfoRoles = new Set(viewUserFullInfoRoles); + const isAllowedToViewFullInfo = currentUserRoles.some((role) => + viewInfoRoles.has(role) + ); const getUserAvatarUrl = (username) => { const host = RCInstance.getHost(); return `${host}/avatar/${username}`; @@ -96,6 +102,24 @@ const UserInformation = () => { /> {currentUserInfo?.username}
+ {currentUserInfo?.statusText && ( + + {currentUserInfo?.statusText} + + )} + {currentUserInfo?.nickname && ( + + )} {currentUserInfo?.roles?.length && ( { ))} } - isAdmin={isAdmin} + isAdmin={isAllowedToViewFullInfo} authenticatedUserId={authenticatedUserId} currentUserInfo={currentUserInfo} /> @@ -121,7 +145,7 @@ const UserInformation = () => { @@ -132,17 +156,26 @@ const UserInformation = () => { ? 'Never' : formatTimestamp(currentUserInfo.lastLogin) } - isAdmin={isAdmin} + isAdmin={isAllowedToViewFullInfo} authenticatedUserId={authenticatedUserId} currentUserInfo={currentUserInfo} /> + {currentUserInfo?.bio && ( + + )} ( @@ -158,14 +191,14 @@ const UserInformation = () => { ))} - isAdmin={isAdmin} + isAdmin={isAllowedToViewFullInfo} authenticatedUserId={authenticatedUserId} currentUserInfo={currentUserInfo} /> From f6eaf878afd12d074ec002ef05fd24db2c1e152c Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:47:35 +0530 Subject: [PATCH 35/76] Fix: Editing of Audio, Video and File Message (#697) * updated handleEditMessage functionality, added valdation for audio, video and file message * I have hidden the edit option for the audio/video message, and reverted previous approach of showing popup message * fix: apply Prettier formatting * Update visibility logic for 'Edit' button * wrapped logic in the variable 'isVisibleForMessageType' for this check and used it --- packages/react/src/views/Message/MessageToolbox.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/Message/MessageToolbox.js b/packages/react/src/views/Message/MessageToolbox.js index a3e2c1ec71..710be2b5a2 100644 --- a/packages/react/src/views/Message/MessageToolbox.js +++ b/packages/react/src/views/Message/MessageToolbox.js @@ -75,11 +75,17 @@ export const MessageToolbox = ({ }; const isAllowedToPin = userRoles.some((role) => pinRoles.has(role)); + const isAllowedToEditMessage = userRoles.some((role) => editMessageRoles.has(role) ) ? true : message.u._id === authenticatedUserId; + + const isVisibleForMessageType = + message.files?.[0].type !== 'audio/mpeg' && + message.files?.[0].type !== 'video/mp4'; + const options = useMemo( () => ({ reply: { @@ -130,7 +136,7 @@ export const MessageToolbox = ({ id: 'edit', onClick: () => handleEditMessage(message), iconName: 'edit', - visible: isAllowedToEditMessage, + visible: isAllowedToEditMessage && isVisibleForMessageType, color: isEditing ? 'secondary' : 'default', ghost: !isEditing, }, From f3b33bc20c5de1d3b71d92aee11a66a73ce5ffad Mon Sep 17 00:00:00 2001 From: Smriti Doneria Date: Wed, 1 Jan 2025 18:52:10 +0530 Subject: [PATCH 36/76] Fix UI Not Updating Immediately After Pinning/Unpinning Messages (#654) * pin * removed comment --- packages/react/src/views/Message/Message.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index a17a3c3a3c..6aed12ef0c 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -102,10 +102,12 @@ const Message = ({ const handlePinMessage = async (msg) => { const isPinned = msg.pinned; + msg.pinned = !isPinned; const pinOrUnpin = isPinned ? await RCInstance.unpinMessage(msg._id) : await RCInstance.pinMessage(msg._id); if (pinOrUnpin.error) { + msg.pinned = isPinned; dispatchToastMessage({ type: 'error', message: 'Error pinning message', From de1036c181e856f3d1858d69332237099ac2416d Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:59:15 +0530 Subject: [PATCH 37/76] fix: image gallery showing error (#759) --- packages/api/src/EmbeddedChatApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index b9a8cef326..c2fdabb694 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -714,7 +714,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch( - `${this.host}/api/v1/channels.images?roomId=${this.rid}`, + `${this.host}/api/v1/rooms.images?roomId=${this.rid}`, { headers: { "Content-Type": "application/json", From d920d596f02a0f4d10261d57fad81daa86788e93 Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:10:42 +0530 Subject: [PATCH 38/76] Feat: add announcement , display room avatar , make room name clickable. (#734) * Added room announcement feature , display avatar and make room name clickable * remove showChannelAvatar from ECOptions * Add padding * Add padding to modal * Remove text underline in announcement in normal view * add showAnnouncement dependency to useEffect * Run prettier * replace channelInfo.description --- packages/react/src/views/ChatBody/ChatBody.js | 75 +++++++++++++++++-- .../src/views/ChatBody/ChatBody.styles.js | 20 ++++- .../react/src/views/ChatHeader/ChatHeader.js | 49 +++++++++--- .../src/views/ChatHeader/ChatHeader.styles.js | 11 ++- packages/react/src/views/EmbeddedChat.js | 4 + .../views/RoomInformation/RoomInformation.js | 52 ++++++------- .../RoomInformation/RoomInformation.styles.js | 25 +++++++ 7 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 packages/react/src/views/RoomInformation/RoomInformation.styles.js diff --git a/packages/react/src/views/ChatBody/ChatBody.js b/packages/react/src/views/ChatBody/ChatBody.js index a582244d0f..721c2d2dc1 100644 --- a/packages/react/src/views/ChatBody/ChatBody.js +++ b/packages/react/src/views/ChatBody/ChatBody.js @@ -1,11 +1,19 @@ /* eslint-disable no-shadow */ -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import React, { + useCallback, + useContext, + useEffect, + useState, + useRef, +} from 'react'; import PropTypes from 'prop-types'; import { css } from '@emotion/react'; import { Box, Throbber, useComponentOverrides, + Modal, + useTheme, } from '@embeddedchat/ui-elements'; import RCContext from '../../context/RCInstance'; import { @@ -33,21 +41,23 @@ const ChatBody = ({ scrollToBottom, }) => { const { classNames, styleOverrides } = useComponentOverrides('ChatBody'); - - const styles = getChatbodyStyles(); + const { theme, mode } = useTheme(); + const styles = getChatbodyStyles(theme, mode); const [scrollPosition, setScrollPosition] = useState(0); const [popupVisible, setPopupVisible] = useState(false); const [, setIsUserScrolledUp] = useState(false); const [otherUserMessage, setOtherUserMessage] = useState(false); - + const [isOverflowing, setIsOverflowing] = useState(false); const { RCInstance, ECOptions } = useContext(RCContext); + const showAnnouncement = ECOptions?.showAnnouncement; const messages = useMessageStore((state) => state.messages); const threadMessages = useMessageStore((state) => state.threadMessages); - + const [isModalOpen, setModalOpen] = useState(false); const setThreadMessages = useMessageStore((state) => state.setThreadMessages); const upsertMessage = useMessageStore((state) => state.upsertMessage); const removeMessage = useMessageStore((state) => state.removeMessage); const isChannelPrivate = useChannelStore((state) => state.isChannelPrivate); + const channelInfo = useChannelStore((state) => state.channelInfo); const isLoginIn = useLoginStore((state) => state.isLoginIn); const [isThreadOpen, threadMainMessage] = useMessageStore((state) => [ @@ -182,7 +192,24 @@ const ChatBody = ({ const showNewMessagesPopup = () => { setPopupVisible(true); }; + const announcementRef = useRef(null); + + const toggleModal = () => { + setModalOpen(!isModalOpen); + }; + + const checkOverflow = () => { + if (announcementRef.current) { + setIsOverflowing( + announcementRef.current.scrollWidth > + announcementRef.current.clientWidth + ); + } + }; + useEffect(() => { + checkOverflow(); + }, [channelInfo.announcement, showAnnouncement]); useEffect(() => { const currentRef = messageListRef.current; currentRef.addEventListener('scroll', handleScroll); @@ -204,6 +231,44 @@ const ChatBody = ({ return ( <> + {channelInfo.announcement && showAnnouncement && ( + + + {channelInfo.announcement} + + + )} + {isModalOpen && ( + + + Announcement + + + + {channelInfo.announcement} + + + )} { +export const getChatbodyStyles = (theme, mode) => { const styles = { chatbodyContainer: css` flex: 1; @@ -14,6 +15,23 @@ export const getChatbodyStyles = () => { padding-top: 70px; margin-top: 0.25rem; `, + announcementStyles: css` + display: flex; + justify-content: center; + padding: 7px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + background-color: ${mode === 'light' + ? lighten(theme.colors.info, 0.78) + : darken(theme.colors.primary, 0.7)}; + `, + announcementTextBox: css` + max-width: 80%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + `, }; return styles; diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 0107399771..817ff40975 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useMemo } from 'react'; +import { css } from '@emotion/react'; import PropTypes from 'prop-types'; import { Box, @@ -8,6 +9,7 @@ import { useToastBarDispatch, useComponentOverrides, useTheme, + Avatar, } from '@embeddedchat/ui-elements'; import { useRCContext } from '../../context/RCInstance'; import { @@ -115,7 +117,10 @@ const ChatHeader = ({ ); const setShowAllFiles = useFileStore((state) => state.setShowAllFiles); const setShowMentions = useMentionsStore((state) => state.setShowMentions); - + const getChannelAvatarURL = (channelname) => { + const host = RCInstance.getHost(); + return `${host}/avatar/${channelname}`; + }; const handleGoBack = async () => { if (isUserAuthenticated) { getMessagesAndRoles(); @@ -347,7 +352,6 @@ const ChatHeader = ({ > - {isUserAuthenticated ? ( <> @@ -355,17 +359,40 @@ const ChatHeader = ({ level={3} className="ec-chat-header--channelName" css={styles.clearSpacing} + style={{ + display: 'flex', + alignItems: 'center', + gap: '0.2rem', + }} > - {channelInfo.name || channelName || 'channelName'} + + + setExclusiveState(setShowChannelinfo)} + > + +
+ {channelInfo.name || channelName || 'channelName'} +
+
+ {fullScreen && ( + + {channelInfo.topic || ''} + + )} +
- {fullScreen && ( -

- {channelInfo.description || ''} -

- )} ) : ( { margin: 0; padding: 0; `, - chatHeaderChild: css` ${rowCentreAlign} padding: 0 0.75rem; @@ -43,6 +42,16 @@ const getChatHeaderStyles = ({ theme, mode }) => { position:relative; gap: 0.5rem; `, + channelName: css` + display: flex; + align-items: center; + gap: 0.1rem; + cursor: pointer; + `, + channelTopic: css` + opacity: 0.8rem; + font-size: 1rem; + `, }; return styles; }; diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index 00557f289c..17b1ff1a41 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -44,6 +44,7 @@ const EmbeddedChat = (props) => { toastBarPosition = 'bottom right', showRoles = false, showAvatar = true, + showAnnouncement = true, showUsername = false, showName = true, enableThreads = false, @@ -204,6 +205,7 @@ const EmbeddedChat = (props) => { showName, showRoles, showAvatar, + showAnnouncement, showUsername, hideHeader, anonymousMode, @@ -219,6 +221,7 @@ const EmbeddedChat = (props) => { showName, showRoles, showAvatar, + showAnnouncement, showUsername, hideHeader, anonymousMode, @@ -281,6 +284,7 @@ EmbeddedChat.propTypes = { toastBarPosition: PropTypes.string, showRoles: PropTypes.bool, showAvatar: PropTypes.bool, + showAnnouncement: PropTypes.bool, enableThreads: PropTypes.bool, theme: PropTypes.object, auth: PropTypes.oneOfType([ diff --git a/packages/react/src/views/RoomInformation/RoomInformation.js b/packages/react/src/views/RoomInformation/RoomInformation.js index 3cfdefad08..b4e26764c9 100644 --- a/packages/react/src/views/RoomInformation/RoomInformation.js +++ b/packages/react/src/views/RoomInformation/RoomInformation.js @@ -9,11 +9,12 @@ import { } from '@embeddedchat/ui-elements'; import RCContext from '../../context/RCInstance'; import { useChannelStore } from '../../store'; +import getRoomInformationStyles from './RoomInformation.styles'; import useSetExclusiveState from '../../hooks/useSetExclusiveState'; const Roominfo = () => { const { RCInstance } = useContext(RCContext); - + const styles = getRoomInformationStyles(); const channelInfo = useChannelStore((state) => state.channelInfo); const { variantOverrides } = useComponentOverrides('RoomMember'); const viewType = variantOverrides.viewType || 'Sidebar'; @@ -44,34 +45,35 @@ const Roominfo = () => { overflow: auto; `} > - - - # {channelInfo.name} - - - Description - - - {channelInfo.description} - + + + + # {channelInfo.name} + {channelInfo.description && ( + <> + Description + {channelInfo.description} + + )} + {channelInfo.topic && ( + <> + Topic + {channelInfo.topic} + + )} + {channelInfo.announcement && ( + <> + Announcement + {channelInfo.announcement} + + )}
diff --git a/packages/react/src/views/RoomInformation/RoomInformation.styles.js b/packages/react/src/views/RoomInformation/RoomInformation.styles.js new file mode 100644 index 0000000000..9ab039b94a --- /dev/null +++ b/packages/react/src/views/RoomInformation/RoomInformation.styles.js @@ -0,0 +1,25 @@ +import { css } from '@emotion/react'; + +const getRoomInformationStyles = () => { + const styles = { + infoContainer: css` + margin: 16px; + display: flex; + flex-direction: column; + gap: 0.1rem; + `, + infoHeader: css` + margin-block: 5px; + font-weight: 900; + `, + info: css` + word-wrap: break-word; + overflow-wrap: anywhere; + white-space: normal; + `, + }; + + return styles; +}; + +export default getRoomInformationStyles; From 1c01bbdc444f35422422bae2dd3521685fa2095e Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:11:04 +0530 Subject: [PATCH 39/76] fix: user action messages (#746) * fix: user action messages * remove showUsername --- packages/react/src/views/Message/Message.js | 5 ++++- packages/react/src/views/Message/MessageHeader.js | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 6aed12ef0c..140cbdc963 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -207,7 +207,10 @@ const Message = ({ isPinned={isPinned} /> )} - + {shouldShowHeader && ( 0 ? message.msg : '(none)' + }`; + case 'room_changed_description': + return `changed description to: ${ + message?.msg && message.msg.length > 0 ? message.msg : '(none)' + }`; + case 'room_changed_topic': + return `changed topic to: ${ + message?.msg && message.msg.length > 0 ? message.msg : '(none)' + }`; default: return ''; } From 051dfe377bbf12655de0978b5cbf276df7094fa2 Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:13:45 +0530 Subject: [PATCH 40/76] Display user roles (Admin, Leader, Moderator, Owner) next to message headers (#738) --- packages/react/src/views/ChatLayout/ChatLayout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/views/ChatLayout/ChatLayout.js b/packages/react/src/views/ChatLayout/ChatLayout.js index ee6b0c1b94..60c935a360 100644 --- a/packages/react/src/views/ChatLayout/ChatLayout.js +++ b/packages/react/src/views/ChatLayout/ChatLayout.js @@ -38,7 +38,7 @@ const ChatLayout = () => { const { classNames, styleOverrides } = useComponentOverrides('ChatBody'); const { RCInstance, ECOptions } = useRCContext(); const anonymousMode = ECOptions?.anonymousMode; - const showRoles = ECOptions?.anonymousMode; + const showRoles = ECOptions?.showRoles; const setStarredMessages = useStarredMessageStore( (state) => state.setStarredMessages ); From 470588888738fbb07d1e96cb64f80e7e285aac60 Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Wed, 1 Jan 2025 19:24:22 +0530 Subject: [PATCH 41/76] feat: add password visibility toggle feature in API development portal (#706) * feat: add password visibility toggle feature in API development portal * Simplify changes --------- Co-authored-by: Zishan Ahmad --- packages/api/playground/index.html | 11 +++++++++++ packages/api/playground/playground.js | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/api/playground/index.html b/packages/api/playground/index.html index 6091350192..d850068e98 100644 --- a/packages/api/playground/index.html +++ b/packages/api/playground/index.html @@ -24,6 +24,14 @@ .playground-output #output{ white-space: pre-wrap; } + #togglePassword { + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + vertical-align: middle; + width: 50px; + } @@ -43,6 +51,9 @@
+
diff --git a/packages/api/playground/playground.js b/packages/api/playground/playground.js index e280d9a4c9..d572018423 100644 --- a/packages/api/playground/playground.js +++ b/packages/api/playground/playground.js @@ -1,4 +1,5 @@ import EmbeddedChatApi from '../src/EmbeddedChatApi'; + let messages = []; async function saveToken(token) { localStorage.setItem("ec_token", token); @@ -96,6 +97,7 @@ const callApi = async (e) => { const result = await api[fn].apply(api, params); printResult(result); } + window.addEventListener('DOMContentLoaded', () => { console.log('Ready') document.getElementById("loginWithPassword").addEventListener("click", loginWithPassword) @@ -113,8 +115,25 @@ window.addEventListener('DOMContentLoaded', () => { document.getElementById("logoutBtn").addEventListener("click", () => api.auth.logout()) document.getElementById("call-api").addEventListener("click", callApi) + const passwordField = document.getElementById('password') + const togglePassword = document.getElementById('togglePassword') + togglePassword.addEventListener('click',() => toggle(passwordField, togglePassword)) }) +let isPasswordVisible = false + +const toggle = (passwordField, togglePassword) => { + isPasswordVisible = !isPasswordVisible + + if(isPasswordVisible){ + passwordField.type = "text" + togglePassword.innerText = "Hide"; + } else { + passwordField.type = "password"; + togglePassword.innerText = "Show"; + } +} + function escapeHTML(str) { return str.replace( /[&<>'"]/g, From 5e3e82e76897d7af647ec488bb4329fc226debf6 Mon Sep 17 00:00:00 2001 From: Raja Majumdar Date: Wed, 1 Jan 2025 19:27:18 +0530 Subject: [PATCH 42/76] feat: insert links in chat (#682) * feat: insert links in chat * fix lint --------- Co-authored-by: Zishan Ahmad --- .../src/store/chatInputItemsStore.js | 2 +- .../src/views/ChatInput/ChatInputToolbar.jsx | 9 +++- .../src/views/ThemeLab/LayoutSetting.jsx | 11 +++- .../src/views/ChatInput/ChatInput.styles.js | 27 ++++++++++ .../ChatInput/ChatInputFormattingToolbar.js | 42 ++++++++++++++- .../src/views/ChatInput/InsertLinkToolBox.js | 54 +++++++++++++++++++ .../src/views/EmojiPicker/EmojiPicker.js | 2 +- 7 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 packages/react/src/views/ChatInput/InsertLinkToolBox.js diff --git a/packages/layout_editor/src/store/chatInputItemsStore.js b/packages/layout_editor/src/store/chatInputItemsStore.js index 1a4b948bfa..7d140d26f0 100644 --- a/packages/layout_editor/src/store/chatInputItemsStore.js +++ b/packages/layout_editor/src/store/chatInputItemsStore.js @@ -1,7 +1,7 @@ import { create } from 'zustand'; const useChatInputItemsStore = create((set) => ({ - surfaceItems: ['emoji', 'formatter', 'audio', 'video', 'file'], + surfaceItems: ['emoji', 'formatter', 'link', 'audio', 'video', 'file'], formatters: ['bold', 'italic', 'strike', 'code', 'multiline'], setSurfaceItems: (items) => set({ surfaceItems: items }), setFormatters: (items) => set({ formatters: items }), diff --git a/packages/layout_editor/src/views/ChatInput/ChatInputToolbar.jsx b/packages/layout_editor/src/views/ChatInput/ChatInputToolbar.jsx index f594e3a813..9d5550c718 100644 --- a/packages/layout_editor/src/views/ChatInput/ChatInputToolbar.jsx +++ b/packages/layout_editor/src/views/ChatInput/ChatInputToolbar.jsx @@ -41,6 +41,13 @@ const ChatInputToolbar = () => { iconName: 'emoji', visible: true, }, + link: { + label: 'Link', + id: 'link', + onClick: () => {}, + iconName: 'link', + visible: true, + }, audio: { label: 'Audio Message', id: 'audio', @@ -61,7 +68,7 @@ const ChatInputToolbar = () => { onClick: () => {}, iconName: 'attachment', visible: true, - }, + }, formatter: { label: 'Formatter', id: 'formatter', diff --git a/packages/layout_editor/src/views/ThemeLab/LayoutSetting.jsx b/packages/layout_editor/src/views/ThemeLab/LayoutSetting.jsx index 0eba8d6eab..60ac4c4296 100644 --- a/packages/layout_editor/src/views/ThemeLab/LayoutSetting.jsx +++ b/packages/layout_editor/src/views/ThemeLab/LayoutSetting.jsx @@ -217,6 +217,15 @@ const LayoutSetting = () => { iconName: 'emoji', visible: true, }, + link: { + label: 'Link', + id: 'link', + onClick: () => { + addInputSurfaceItem('link'); + }, + iconName: 'link', + visible: true, + }, audio: { label: 'Audio Message', id: 'audio', @@ -243,7 +252,7 @@ const LayoutSetting = () => { }, iconName: 'attachment', visible: true, - }, + }, formatter: { label: 'Formatter', id: 'formatter', diff --git a/packages/react/src/views/ChatInput/ChatInput.styles.js b/packages/react/src/views/ChatInput/ChatInput.styles.js index 9123a4722b..7aa85e40f1 100644 --- a/packages/react/src/views/ChatInput/ChatInput.styles.js +++ b/packages/react/src/views/ChatInput/ChatInput.styles.js @@ -116,3 +116,30 @@ export const getCommonRecorderStyles = (theme) => { return styles; }; + +export const getInsertLinkModalStyles = (theme) => { + const styles = { + inputWithFormattingBox: css` + border: 1px solid ${theme.colors.border}; + border-radius: ${theme.radius}; + margin: 0.5rem 1rem; + &.focused { + border: ${`1.5px solid ${theme.colors.ring}`}; + } + `, + modalHeader: css` + padding: 0 0.5rem; + `, + modalContent: css` + display: flex; + flex-direction: column; + gap: 0.5rem; + margin: 1rem 0; + `, + modalFooter: css` + padding: 0.75rem 1rem; + `, + }; + + return styles; +}; diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index fd96f50363..851034833d 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -15,13 +15,14 @@ import AudioMessageRecorder from './AudioMessageRecorder'; import VideoMessageRecorder from './VideoMessageRecoder'; import { getChatInputFormattingToolbarStyles } from './ChatInput.styles'; import formatSelection from '../../lib/formatSelection'; +import InsertLinkToolBox from './InsertLinkToolBox'; const ChatInputFormattingToolbar = ({ messageRef, inputRef, triggerButton, optionConfig = { - surfaceItems: ['emoji', 'formatter', 'audio', 'video', 'file'], + surfaceItems: ['emoji', 'formatter', 'link', 'audio', 'video', 'file'], formatters: ['bold', 'italic', 'strike', 'code', 'multiline'], }, }) => { @@ -40,6 +41,7 @@ const ChatInputFormattingToolbar = ({ ); const [isEmojiOpen, setEmojiOpen] = useState(false); + const [isInsertLinkOpen, setInsertLinkOpen] = useState(false); const handleClickToOpenFiles = () => { inputRef.current.click(); @@ -54,6 +56,22 @@ const ChatInputFormattingToolbar = ({ triggerButton?.(null, message); }; + const handleAddLink = (linkText, linkUrl) => { + if (!linkText || !linkUrl) { + setInsertLinkOpen(false); + return; + } + + const start = messageRef.current.selectionStart; + const end = messageRef.current.selectionEnd; + const msg = messageRef.current.value; + const hyperlink = `[${linkText}](${linkUrl})`; + const message = msg.slice(0, start) + hyperlink + msg.slice(end); + + triggerButton?.(null, message); + setInsertLinkOpen(false); + }; + const chatToolMap = { emoji: ( @@ -91,6 +109,20 @@ const ChatInputFormattingToolbar = ({ ), + link: ( + + { + setInsertLinkOpen(true); + }} + > + + + + ), formatter: formatters .map((name) => formatter.find((item) => item.name === name)) .map((item) => ( @@ -136,6 +168,14 @@ const ChatInputFormattingToolbar = ({ `} /> )} + + {isInsertLinkOpen && ( + setInsertLinkOpen(false)} + /> + )} ); }; diff --git a/packages/react/src/views/ChatInput/InsertLinkToolBox.js b/packages/react/src/views/ChatInput/InsertLinkToolBox.js new file mode 100644 index 0000000000..49e8e0d4ad --- /dev/null +++ b/packages/react/src/views/ChatInput/InsertLinkToolBox.js @@ -0,0 +1,54 @@ +import React, { useState } from 'react'; +import { Modal, Input, Button, useTheme } from '@embeddedchat/ui-elements'; +import { getInsertLinkModalStyles } from './ChatInput.styles'; + +const InsertLinkToolBox = ({ + handleAddLink, + selectedText, + onClose = () => {}, +}) => { + const { theme } = useTheme(); + const styles = getInsertLinkModalStyles(theme); + const [linkText, setLinkText] = useState(selectedText || 'Text'); + const [linkUrl, setLinkUrl] = useState(null); + + const handleLinkTextOnChange = (e) => { + setLinkText(e.target.value); + }; + const handleLinkUrlOnChange = (e) => { + setLinkUrl(e.target.value); + }; + + return ( + + + Add link + + + + + + + + + + + + ); +}; + +export default InsertLinkToolBox; diff --git a/packages/react/src/views/EmojiPicker/EmojiPicker.js b/packages/react/src/views/EmojiPicker/EmojiPicker.js index e8e2926675..bc56649fdf 100644 --- a/packages/react/src/views/EmojiPicker/EmojiPicker.js +++ b/packages/react/src/views/EmojiPicker/EmojiPicker.js @@ -32,7 +32,7 @@ const CustomEmojiPicker = ({ height="auto" width="auto" > - + Date: Wed, 1 Jan 2025 19:27:59 +0530 Subject: [PATCH 43/76] fix: issue of mentioning the user in the file description (#677) * Fix the issue of mentioning the user in the file description * Removed all unwanted warnings * Formatted with prettier * Adjusted the width of the mentions preview in the file description * fixed the position of mentions preview such that it does not affect the height of file upload modal * Fixed all the inconsistencies * Fixed minor design inconsistency --------- Co-authored-by: Zishan Ahmad --- .../src/views/AttachmentHandler/Attachment.js | 5 +- .../AttachmentHandler/AttachmentMetadata.js | 49 +++++++++---- .../views/AttachmentHandler/Attachments.js | 3 +- .../AttachmentHandler/AudioAttachment.js | 10 ++- .../AttachmentHandler/ImageAttachment.js | 2 + .../AttachmentHandler/VideoAttachment.js | 2 + .../AttachmentPreview/AttachmentPreview.js | 71 +++++++++++++++---- .../AttachmentPreview.styles.js | 18 ++++- .../react/src/views/ChatInput/ChatInput.js | 29 ++++---- packages/react/src/views/Markdown/Markdown.js | 6 +- .../src/views/Mentions/MembersList.styles.js | 2 +- packages/react/src/views/Message/Message.js | 9 ++- 12 files changed, 156 insertions(+), 50 deletions(-) diff --git a/packages/react/src/views/AttachmentHandler/Attachment.js b/packages/react/src/views/AttachmentHandler/Attachment.js index 594bfa7d70..ec752e9bcd 100644 --- a/packages/react/src/views/AttachmentHandler/Attachment.js +++ b/packages/react/src/views/AttachmentHandler/Attachment.js @@ -7,7 +7,7 @@ import AudioAttachment from './AudioAttachment'; import VideoAttachment from './VideoAttachment'; import TextAttachment from './TextAttachment'; -const Attachment = ({ attachment, host, type, variantStyles = {} }) => { +const Attachment = ({ attachment, host, type, variantStyles = {}, msg }) => { const author = { authorIcon: attachment?.author_icon, authorName: attachment?.author_name, @@ -19,6 +19,7 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { host={host} author={author} variantStyles={variantStyles} + msg={msg} /> ); } @@ -29,6 +30,7 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { host={host} author={author} variantStyles={variantStyles} + msg={msg} /> ); } @@ -39,6 +41,7 @@ const Attachment = ({ attachment, host, type, variantStyles = {} }) => { host={host} author={author} variantStyles={variantStyles} + msg={msg} /> ); } diff --git a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js index 428342ba19..5bd249ce5e 100644 --- a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js +++ b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js @@ -1,8 +1,9 @@ import React from 'react'; import { css } from '@emotion/react'; import { ActionButton, Box } from '@embeddedchat/ui-elements'; +import { Markdown } from '../Markdown'; -const AttachmentMetadata = ({ attachment, url, variantStyles = {} }) => { +const AttachmentMetadata = ({ attachment, url, variantStyles = {}, msg }) => { const handleDownload = async () => { try { const response = await fetch(url); @@ -32,15 +33,25 @@ const AttachmentMetadata = ({ attachment, url, variantStyles = {} }) => { variantStyles.attachmentMetaContainer, ]} > -

- {attachment.description} -

+ {msg ? ( + + ) : ( + attachment.description + )} +
{ `} >

{attachment.title}

diff --git a/packages/react/src/views/AttachmentHandler/Attachments.js b/packages/react/src/views/AttachmentHandler/Attachments.js index 35010476a4..f558ad5ec0 100644 --- a/packages/react/src/views/AttachmentHandler/Attachments.js +++ b/packages/react/src/views/AttachmentHandler/Attachments.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Attachment from './Attachment'; import RCContext from '../../context/RCInstance'; -const Attachments = ({ attachments, type, variantStyles = {} }) => { +const Attachments = ({ attachments, type, variantStyles = {}, msg }) => { const { RCInstance } = useContext(RCContext); let host = RCInstance.getHost(); host = host.replace(/\/$/, ''); @@ -15,6 +15,7 @@ const Attachments = ({ attachments, type, variantStyles = {} }) => { host={host} variantStyles={variantStyles} type={type} + msg={msg} /> )); }; diff --git a/packages/react/src/views/AttachmentHandler/AudioAttachment.js b/packages/react/src/views/AttachmentHandler/AudioAttachment.js index 0f5824aa0f..b88d0a41da 100644 --- a/packages/react/src/views/AttachmentHandler/AudioAttachment.js +++ b/packages/react/src/views/AttachmentHandler/AudioAttachment.js @@ -5,7 +5,14 @@ import { Box, Avatar, useTheme } from '@embeddedchat/ui-elements'; import AttachmentMetadata from './AttachmentMetadata'; import RCContext from '../../context/RCInstance'; -const AudioAttachment = ({ attachment, host, type, author, variantStyles }) => { +const AudioAttachment = ({ + attachment, + host, + type, + author, + variantStyles, + msg, +}) => { const { RCInstance } = useContext(RCContext); const { theme } = useTheme(); const getUserAvatarUrl = (icon) => { @@ -58,6 +65,7 @@ const AudioAttachment = ({ attachment, host, type, author, variantStyles }) => { attachment={attachment} url={host + (attachment.title_url || attachment.audio_url)} variantStyles={variantStyles} + msg={msg} />
@@ -101,14 +126,32 @@ const AttachmentPreview = () => { > File description - { - handleFileDescription(e); - }} - value={fileDescription} - css={styles.input} - placeholder="Description" - /> + + + {showMembersList && ( + + )} + + { + handleFileDescription(e); + }} + css={styles.input} + placeholder="Description" + ref={messageRef} + /> +
diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.styles.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.styles.js index 44729be847..45f8cf455a 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.styles.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.styles.js @@ -11,7 +11,7 @@ const getAttachmentPreviewStyles = () => { `, input: css` - width: 95.5%; + width: 100%; `, modalContent: css` @@ -19,6 +19,22 @@ const getAttachmentPreviewStyles = () => { overflow-x: hidden; max-height: 350px; `, + + fileDescription: css` + width: 100%; + position: relative; + z-index: 1300; + `, + + mentionListContainer: css` + position: absolute; + top: -100px; + width: 100%; + max-height: 100px; + overflow-y: auto; + background: white; + z-index: 1400; + `, }; return styles; diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 86b4b5082a..91fc98deca 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -469,18 +469,23 @@ const ChatInput = ({ scrollToBottom }) => { } /> ) : null} - - {showMembersList && ( - - )} + + {showMembersList && ( + + )} + {showCommandList && ( { +const Markdown = ({ body, md, isReaction = false }) => { const members = useMemberStore((state) => state.members); const username = useUserStore((state) => state.username); const value = useMemo(() => ({ members, username }), [members, username]); @@ -23,12 +23,12 @@ const Markdown = ({ body, isReaction = false }) => { ); } - if (!body || !body.md) return <>; + if (!body || !md) return <>; return ( - + ); diff --git a/packages/react/src/views/Mentions/MembersList.styles.js b/packages/react/src/views/Mentions/MembersList.styles.js index e288cae04d..03bc8f4018 100644 --- a/packages/react/src/views/Mentions/MembersList.styles.js +++ b/packages/react/src/views/Mentions/MembersList.styles.js @@ -3,7 +3,7 @@ import { css } from '@emotion/react'; const getMemberListStyles = (theme) => { const styles = { main: css` - margin: 0.2rem 2rem; + margin: 0.2rem 0rem; display: block; overflow: auto; max-height: 10rem; diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 140cbdc963..2a56b20b76 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -233,14 +233,19 @@ const Message = ({ > {message.attachments && message.attachments.length > 0 ? ( <> - + ) : ( - + )} {message.blocks && ( From 3e1360e3302e918a430c053364fda1fe41e155e9 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:17:00 +0530 Subject: [PATCH 44/76] Fix: Capitalize the first letter of user roles for improved UI (#798) --- packages/react/src/views/Message/MessageHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/views/Message/MessageHeader.js b/packages/react/src/views/Message/MessageHeader.js index 594c435113..89206256ea 100644 --- a/packages/react/src/views/Message/MessageHeader.js +++ b/packages/react/src/views/Message/MessageHeader.js @@ -127,7 +127,7 @@ const MessageHeader = ({ css={styles.userRole} className={appendClassNames('ec-message-user-role')} > - admin + Admin )} @@ -138,7 +138,7 @@ const MessageHeader = ({ css={styles.userRole} className={appendClassNames('ec-message-user-role')} > - {role} + {role.charAt(0).toUpperCase() + role.slice(1)} ))} From 843adf0836cbb8c6996e805839f4e330e63b7b16 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:25:24 +0530 Subject: [PATCH 45/76] Fix bug: Display user roles in MessageAggregator when showRoles is true (#790) --- .../src/views/MessageAggregators/common/MessageAggregator.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index c70f3d73a5..241c0aa576 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -17,6 +17,7 @@ import LoadingIndicator from './LoadingIndicator'; import NoMessagesIndicator from './NoMessageIndicator'; import FileDisplay from '../../FileMessage/FileMessage'; import useSetExclusiveState from '../../../hooks/useSetExclusiveState'; +import { useRCContext } from '../../../context/RCInstance'; export const MessageAggregator = ({ title, @@ -33,6 +34,8 @@ export const MessageAggregator = ({ const { theme } = useTheme(); const styles = getMessageAggregatorStyles(theme); const setExclusiveState = useSetExclusiveState(); + const { ECOptions } = useRCContext(); + const showRoles = ECOptions?.showRoles; const messages = useMessageStore((state) => state.messages); const threadMessages = useMessageStore((state) => state.threadMessages) || []; const allMessages = useMemo( @@ -126,7 +129,7 @@ export const MessageAggregator = ({ type="default" showAvatar showToolbox={false} - showRoles={false} + showRoles={showRoles} isInSidebar style={{ flex: 1, From 73f8fc08041f9190e541709f178a594f7543dc54 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:42:49 +0530 Subject: [PATCH 46/76] Fix: Disable hover effect for User Action Messages (#795) --- packages/react/src/views/Message/Message.js | 18 ++++++++++++++++++ .../react/src/views/Message/Message.styles.js | 9 +-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 2a56b20b76..03c558c761 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -7,6 +7,8 @@ import { useComponentOverrides, appendClassNames, useTheme, + lighten, + darken, } from '@embeddedchat/ui-elements'; import { Attachments } from '../AttachmentHandler'; import { Markdown } from '../Markdown'; @@ -72,8 +74,23 @@ const Message = ({ })); const isMe = message.u._id === authenticatedUserId; + const theme = useTheme(); + const { mode } = useTheme(); const styles = getMessageStyles(theme); + const hasType = Boolean(message.t); + + const hoverStyle = hasType + ? {} + : { + '&:hover': { + backgroundColor: + mode === 'light' + ? darken(theme.theme.colors.background, 0.03) + : lighten(theme.theme.colors.background, 1), + }, + }; + const bubbleStyles = useBubbleStyles(isMe); const pinRoles = new Set(pinPermissions); const editMessageRoles = new Set(editMessagePermissions); @@ -195,6 +212,7 @@ const Message = ({ className={appendClassNames('ec-message', classNames)} css={[ variantStyles.messageParent || styles.main, + hoverStyle, editMessage._id === message._id && styles.messageEditing, ]} style={styleOverrides} diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index 9f9358fd92..8c380d241b 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -1,7 +1,6 @@ import { css } from '@emotion/react'; -import { lighten, darken } from '@embeddedchat/ui-elements'; -export const getMessageStyles = ({ theme, mode }) => { +export const getMessageStyles = ({ theme }) => { const styles = { main: css` display: flex; @@ -12,12 +11,6 @@ export const getMessageStyles = ({ theme, mode }) => { padding-left: 2.25rem; padding-right: 2.25rem; color: ${theme.colors.foreground}; - - &:hover { - background-color: ${mode === 'light' - ? darken(theme.colors.background, 0.03) - : lighten(theme.colors.background, 1)}; - } `, messageEditing: css` background-color: ${theme.colors.secondary}; From dd0657f299412a71c6a9950aeda91240ad1c368c Mon Sep 17 00:00:00 2001 From: Piyush <157290995+thepiyush-303@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:44:55 +0530 Subject: [PATCH 47/76] fix: jump to message highlights the element (#748) * add highlight color * lint * consume theme --- .../views/MessageAggregators/common/MessageAggregator.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 241c0aa576..3411d54107 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -54,7 +54,11 @@ export const MessageAggregator = ({ const element = document.getElementById(`ec-message-body-${msgId}`); if (element) { setShowSidebar(false); - element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + element.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + element.style.backgroundColor = theme.colors.warning; + setTimeout(() => { + element.style.backgroundColor = ''; + }, 1000); } } }; From bd6cefb81da9d8b3fb5d1a30453364065fdb29b5 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:45:42 +0530 Subject: [PATCH 48/76] Fix: The 'Copy' option functionality is now working for File messages (#716) * Fix: 'Copy' option functionality is not working for File messages * Updated logic with copy file name in case of description not present --- packages/react/src/views/Message/Message.js | 30 ++++++++++++--------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index 03c558c761..fa8c864fec 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -138,20 +138,24 @@ const Message = ({ }; const handleCopyMessage = async (msg) => { - navigator.clipboard - .writeText(msg.msg) - .then(() => { - dispatchToastMessage({ - type: 'success', - message: 'Message copied successfully', - }); - }) - .catch(() => { - dispatchToastMessage({ - type: 'error', - message: 'Error in copying message', - }); + const textToCopy = + msg.msg || + (msg.attachments && msg.attachments[0] + ? msg.attachments[0].description || msg.attachments[0].title + : ''); + + try { + await navigator.clipboard.writeText(textToCopy); + dispatchToastMessage({ + type: 'success', + message: 'Message copied successfully', }); + } catch (error) { + dispatchToastMessage({ + type: 'error', + message: 'Error in copying message', + }); + } }; const getMessageLink = async (id) => { From 12cefd6a031645ac77673484ad5ba70dd90ee586 Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:56:08 +0530 Subject: [PATCH 49/76] feat: Add Syntax Highlighting to Multiline Code Blocks using react-syntax-highlighter (#764) * add code syntax highlighting * Run prettier * remove unecessary space in package.json * Make multiline code occupy full width * use react-syntax-highlighter * Remove theme state and useEffect --- packages/markups/package.json | 3 +- packages/markups/src/elements/CodeBlock.js | 19 +++++-- packages/markups/src/elements/CodeElement.js | 14 +++-- .../markups/src/elements/elements.styles.js | 57 +++++++++++++------ yarn.lock | 24 ++++++++ 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/packages/markups/package.json b/packages/markups/package.json index 3b09bc97fc..2c617f5266 100644 --- a/packages/markups/package.json +++ b/packages/markups/package.json @@ -76,6 +76,7 @@ "@emotion/react": "11.7.1", "@rollup/plugin-json": "^6.0.0", "emoji-toolkit": "^7.0.1", - "prop-types": "^15.8.1" + "prop-types": "^15.8.1", + "react-syntax-highlighter": "^15.6.1" } } diff --git a/packages/markups/src/elements/CodeBlock.js b/packages/markups/src/elements/CodeBlock.js index b8f5acf133..54bad69fcb 100644 --- a/packages/markups/src/elements/CodeBlock.js +++ b/packages/markups/src/elements/CodeBlock.js @@ -1,9 +1,13 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { Box } from '@embeddedchat/ui-elements'; -import { CodeBlockStyles as styles } from './elements.styles'; +import { Box, useTheme } from '@embeddedchat/ui-elements'; +import SyntaxHighlighter from 'react-syntax-highlighter'; +import { vs, monokai } from 'react-syntax-highlighter/dist/esm/styles/hljs'; +import { CodeBlockStyles } from './elements.styles'; const CodeBlock = ({ lines }) => { + const { mode } = useTheme(); + const styles = CodeBlockStyles(); const code = useMemo( () => lines.map((line) => line.value.value).join('\n'), [lines] @@ -14,7 +18,13 @@ const CodeBlock = ({ lines }) => { ``` - {code} + + {code} + ``` @@ -23,7 +33,6 @@ const CodeBlock = ({ lines }) => { }; export default CodeBlock; - CodeBlock.propTypes = { lines: PropTypes.any, }; diff --git a/packages/markups/src/elements/CodeElement.js b/packages/markups/src/elements/CodeElement.js index 043f5a2a00..8a2483f17f 100644 --- a/packages/markups/src/elements/CodeElement.js +++ b/packages/markups/src/elements/CodeElement.js @@ -1,12 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import PlainSpan from './PlainSpan'; +import { InlineElementsStyles } from './elements.styles'; -const CodeElement = ({ contents }) => ( - - - -); +const CodeElement = ({ contents }) => { + const styles = InlineElementsStyles(); + return ( + + + + ); +}; export default CodeElement; diff --git a/packages/markups/src/elements/elements.styles.js b/packages/markups/src/elements/elements.styles.js index aa44d77dd1..2f1f2fb295 100644 --- a/packages/markups/src/elements/elements.styles.js +++ b/packages/markups/src/elements/elements.styles.js @@ -1,23 +1,46 @@ import { css } from '@emotion/react'; -import { useTheme } from '@embeddedchat/ui-elements'; +import { useTheme, darken } from '@embeddedchat/ui-elements'; -export const CodeBlockStyles = { - copyonly: css` - display: none; - width: 100%; - height: 0; - user-select: none; - vertical-align: baseline; - font-size: 0; - -moz-box-orient: vertical; - `, +export const InlineElementsStyles = () => { + const { theme } = useTheme(); + const styles = { + inlineElement: css` + font-weight: 600; + font-size: 0.75rem; + width: fit-content; + padding: 3px; + background-color: ${theme.colors.border}; + border-radius: 6px; + `, + }; + return styles; +}; +export const CodeBlockStyles = () => { + const { theme } = useTheme(); + const styles = { + copyonly: css` + display: none; + width: 100%; + height: 0; + user-select: none; + vertical-align: baseline; + font-size: 0; + -moz-box-orient: vertical; + `, - prestyle: css` - display: inline-block; - max-width: 100%; - overflow-x: auto; - white-space: pre-wrap; - `, + prestyle: css` + display: inline-block; + width: 100%; + overflow-x: auto; + white-space: pre-wrap; + `, + codeBlock: css` + background-color: ${darken(theme.colors.accent, 0.01)} !important; + border-radius: ${theme.radius}; + font-weight: 600; + `, + }; + return styles; }; export const ColorElementStyles = { diff --git a/yarn.lock b/yarn.lock index 39464bc873..585c7d5679 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2366,6 +2366,7 @@ __metadata: prop-types: ^15.8.1 react: ^17.0.2 react-dom: ^17.0.2 + react-syntax-highlighter: ^15.6.1 rimraf: ^5.0.1 rollup: ^2.70.1 rollup-plugin-analyzer: ^4.0.0 @@ -18694,6 +18695,13 @@ __metadata: languageName: node linkType: hard +"highlightjs-vue@npm:^1.0.0": + version: 1.0.0 + resolution: "highlightjs-vue@npm:1.0.0" + checksum: 895f2dd22c93a441aca7df8d21f18c00697537675af18832e50810a071715f79e45eda677e6244855f325234c6a06f7bd76f8f20bd602040fc350c80ac7725e4 + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -27213,6 +27221,22 @@ __metadata: languageName: node linkType: hard +"react-syntax-highlighter@npm:^15.6.1": + version: 15.6.1 + resolution: "react-syntax-highlighter@npm:15.6.1" + dependencies: + "@babel/runtime": ^7.3.1 + highlight.js: ^10.4.1 + highlightjs-vue: ^1.0.0 + lowlight: ^1.17.0 + prismjs: ^1.27.0 + refractor: ^3.6.0 + peerDependencies: + react: ">= 0.14.0" + checksum: 417b6f1f2e0c1e00dcc12d34da457b94c7419345306a951d0a8d2d031a0c964179d6b700137870ad1397572cbc3a4454e94de7bbef914a81674edae2098f02dc + languageName: node + linkType: hard + "react@npm:18.2.0, react@npm:^18.2.0": version: 18.2.0 resolution: "react@npm:18.2.0" From 9352c030224059a865a7c0da1905e3940824ce86 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:17:56 +0530 Subject: [PATCH 50/76] Feat: Added 'Collapse & Uncollapse' Functionality for Audio, Video Messages & Image attachment (#772) * BUG: Fix extra top space for audio and video messages to improve UI consistency * Fix: Add padding for audio and video messages in nested attachments and quotes * added conditional CSS handling the description present or absent case * Same logic for conditional CSS but written in better way * Feat: Add 'Collapse & Uncollapse' functionality for Audio and Video messages * Feat: Add 'Collapse & Uncollapse' functionality * Feat: Add 'Collapse & Uncollapse' functionality * Added Collapse & Uncollapse Functionality for Image Attachment * Using chevron-down, chevron-left instead of arrow-down, arrow-right --- .../AttachmentHandler/AttachmentMetadata.js | 19 +++++++++- .../AttachmentHandler/AudioAttachment.js | 14 ++++++- .../AttachmentHandler/ImageAttachment.js | 30 ++++++++++----- .../AttachmentHandler/VideoAttachment.js | 38 ++++++++++++------- 4 files changed, 74 insertions(+), 27 deletions(-) diff --git a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js index 5bd249ce5e..390cab7b71 100644 --- a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js +++ b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js @@ -3,7 +3,14 @@ import { css } from '@emotion/react'; import { ActionButton, Box } from '@embeddedchat/ui-elements'; import { Markdown } from '../Markdown'; -const AttachmentMetadata = ({ attachment, url, variantStyles = {}, msg }) => { +const AttachmentMetadata = ({ + attachment, + url, + variantStyles = {}, + msg, + onExpandCollapseClick, + isExpanded, +}) => { const handleDownload = async () => { try { const response = await fetch(url); @@ -78,6 +85,16 @@ const AttachmentMetadata = ({ attachment, url, variantStyles = {}, msg }) => { > {attachment.title}

+ { + setIsExpanded((prevState) => !prevState); + }; + return ( -