From b9d2722e41067f218fd3adf26a2dd46d412e11a5 Mon Sep 17 00:00:00 2001 From: Devansh Date: Mon, 16 Sep 2024 18:04:11 +0530 Subject: [PATCH 001/153] 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 002/153] 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 003/153] 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 004/153] 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 005/153] 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 006/153] 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 007/153] 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 008/153] 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 009/153] 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 010/153] [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 011/153] 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 012/153] 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 013/153] 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 014/153] 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 015/153] 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 016/153] 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 017/153] 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 018/153] 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 019/153] 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 020/153] 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 021/153] 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 022/153] 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 023/153] 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 024/153] 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 025/153] 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 026/153] 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 027/153] 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 034/153] 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 035/153] 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 036/153] 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 037/153] 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 038/153] 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 039/153] 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 040/153] 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 041/153] 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 042/153] 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 043/153] 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 044/153] 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 045/153] 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 046/153] 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 047/153] 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 048/153] 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 049/153] 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 050/153] 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 ( - - - {title} + + {messageDescription(title)} {isHeaderIcon && } diff --git a/packages/react/src/views/DynamicHeader/DynamicHeader.styles.js b/packages/react/src/views/DynamicHeader/DynamicHeader.styles.js index 1df4bf2206..d04577ba57 100644 --- a/packages/react/src/views/DynamicHeader/DynamicHeader.styles.js +++ b/packages/react/src/views/DynamicHeader/DynamicHeader.styles.js @@ -12,7 +12,7 @@ const useDynamicHeaderStyles = () => { `, clearSpacing: css` - margin: 0; + margin-bottom: 1px; padding: 0; text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index 059bf7e853..75253043e2 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -203,7 +203,20 @@ export const MessageMetricsStyles = { display: flex; justify-content: center; align-items: center; - margin-left: ${isFirstMessage ? '0.5rem' : '0.25rem'}; + margin-left: ${isFirstMessage ? '0.5rem' : '0.3rem'}; + margin-top: 1.2px; + `, + + metricsAvatarItem: css` + letter-spacing: 0rem; + font-size: 0.625rem; + font-weight: 700; + line-height: 0.75rem; + display: flex; + justify-content: center; + align-items: center; + margin-left: 10px; + margin-top: 2px; `, metricsItemLabel: css` diff --git a/packages/react/src/views/Message/MessageMetrics.js b/packages/react/src/views/Message/MessageMetrics.js index 07b7e40505..8bc366d71b 100644 --- a/packages/react/src/views/Message/MessageMetrics.js +++ b/packages/react/src/views/Message/MessageMetrics.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { formatDistance } from 'date-fns'; import { Box, @@ -6,8 +6,12 @@ import { Icon, useComponentOverrides, appendClassNames, + Avatar, + Tooltip, } from '@embeddedchat/ui-elements'; import { MessageMetricsStyles as styles } from './Message.styles'; +import RCContext from '../../context/RCInstance'; + import BubbleThreadBtn from './BubbleVariant/BubbleThreadBtn'; export const MessageMetrics = ({ @@ -25,6 +29,18 @@ export const MessageMetrics = ({ style ); + const { RCInstance } = useContext(RCContext); + + const getUserAvatarUrl = (username) => { + const host = RCInstance.getHost(); + return `${host}/avatar/${username}`; + }; + + const participantsList = + (message?.replies?.length ?? 0) - 1 > 0 + ? `+${message.replies.length - 1}` + : null; + return ( - Reply + View thread - - - {message.tcount} - {!!message.tcount && ( - - - - {message.replies.length} - - + <> + + + + {participantsList && ( + + {participantsList} + + )} + + + )} - - - - {formatDistance(new Date(message.tlm), new Date(), { - addSuffix: true, - })} + + + + {message.tcount} replies,{' '} + {new Date(message.tlm).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + hour12: false, + })} + - + ))} From 33c42ecc3a5a936c9fd1d1e4d25974b42931044b Mon Sep 17 00:00:00 2001 From: Dhanya <163339354+dhanyamd@users.noreply.github.com> Date: Sat, 1 Mar 2025 15:45:42 +0530 Subject: [PATCH 088/153] fix: add types to filename and description (#993) --- .../react/src/views/AttachmentPreview/AttachmentPreview.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 82ce58e818..bf20be3f12 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -127,12 +127,13 @@ const AttachmentPreview = () => { `} > File name - + { handleFileName(e); }} value={fileName} + type="text" css={styles.input} placeholder="name" /> @@ -170,8 +171,10 @@ const AttachmentPreview = () => { onChange={(e) => { handleFileDescription(e); }} + type="text" css={styles.input} placeholder="Description" + value={description} ref={messageRef} /> From d7b71392e2626696a9bb80df601cb80bba3bde53 Mon Sep 17 00:00:00 2001 From: Abir Chakraborty <142606190+abirc8010@users.noreply.github.com> Date: Sat, 1 Mar 2025 15:59:26 +0530 Subject: [PATCH 089/153] Fixed Threads Position (#980) --- .../src/views/Thread/ThreadMessageList.js | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/react/src/views/Thread/ThreadMessageList.js b/packages/react/src/views/Thread/ThreadMessageList.js index f6fbf41212..01fd1c5fc5 100644 --- a/packages/react/src/views/Thread/ThreadMessageList.js +++ b/packages/react/src/views/Thread/ThreadMessageList.js @@ -13,27 +13,30 @@ const ThreadMessageList = ({ threadMessages, threadMainMessage }) => { const isMessageNewDay = (current, previous) => !previous || !isSameDay(new Date(current.ts), new Date(previous.ts)); + + const sortedMessages = [...(threadMessages || []), threadMainMessage] + .filter(Boolean) + .sort((a, b) => new Date(a.ts) - new Date(b.ts)); + return ( <> - {threadMessages?.concat(threadMainMessage).map((msg, index, arr) => { - const prev = arr[index + 1]; - const next = arr[index - 1]; + {sortedMessages.map((msg, index, arr) => { + const prev = arr[index - 1]; + const next = arr[index + 1]; const newDay = isMessageNewDay(msg, prev); const sequential = isMessageSequential(msg, prev, 300); const lastSequential = sequential && isMessageLastSequential(msg, next); return ( - msg && ( - - ) + ); })} {showReportMessage && } From 32e60c7dc3e179498d628ae2afab92e1ca6e7660 Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sat, 1 Mar 2025 16:01:39 +0530 Subject: [PATCH 090/153] fix: thread messages appear in main chat window (#979) --- packages/react/src/views/MessageList/MessageList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/views/MessageList/MessageList.js b/packages/react/src/views/MessageList/MessageList.js index 6a13952165..ede0a694bf 100644 --- a/packages/react/src/views/MessageList/MessageList.js +++ b/packages/react/src/views/MessageList/MessageList.js @@ -72,7 +72,7 @@ const MessageList = ({ )} - {messages + {filteredMessages .slice() .reverse() .map((msg, index, arr) => { From dbc08bc2916dbed63cfa1c77cd572ef113b4e3f6 Mon Sep 17 00:00:00 2001 From: Smriti Doneria Date: Sat, 1 Mar 2025 16:06:05 +0530 Subject: [PATCH 091/153] Fix: deletion of message get simultaneously deleted from the starred modal. (#966) * starred fix * refractored file sidebar --- .../src/views/FileMessage/FileMessage.js | 30 ++++++++++++++++--- packages/react/src/views/Message/Message.js | 1 + 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/react/src/views/FileMessage/FileMessage.js b/packages/react/src/views/FileMessage/FileMessage.js index 4944005c0d..1ae977a0ef 100644 --- a/packages/react/src/views/FileMessage/FileMessage.js +++ b/packages/react/src/views/FileMessage/FileMessage.js @@ -1,4 +1,10 @@ -import React, { useState, useCallback, memo, useContext } from 'react'; +import React, { + useState, + useCallback, + memo, + useContext, + useEffect, +} from 'react'; import PropTypes from 'prop-types'; import { Box, @@ -20,16 +26,18 @@ import FilePreviewHeader from './FilePreviewHeader'; import { MessageBody as FileBody } from '../Message/MessageBody'; import { FileMetrics } from './FileMetrics'; import { useRCContext } from '../../context/RCInstance'; -import { useMessageStore } from '../../store'; +import { useChannelStore, useMessageStore } from '../../store'; import { fileDisplayStyles as styles } from './Files.styles'; -const FileMessage = ({ fileMessage }) => { +const FileMessage = ({ fileMessage, onDeleteFile }) => { const { classNames, styleOverrides } = useComponentOverrides('FileMessage'); const dispatchToastMessage = useToastBarDispatch(); const { RCInstance } = useRCContext(); const messages = useMessageStore((state) => state.messages); - + const [files, setFiles] = useState([]); const theme = useTheme(); + const isChannelPrivate = useChannelStore((state) => state.isChannelPrivate); + const [isFetching, setIsFetching] = useState(true); const { mode } = theme; const messageStyles = styles.message; @@ -75,6 +83,19 @@ const FileMessage = ({ fileMessage }) => { }, [messages, RCInstance, dispatchToastMessage] ); + useEffect(() => { + const fetchAllFiles = async () => { + const res = await RCInstance.getAllFiles(isChannelPrivate, ''); + if (res?.files) { + const sortedFiles = res.files.sort( + (a, b) => new Date(b.uploadedAt) - new Date(a.uploadedAt) + ); + setFiles(sortedFiles); + setIsFetching(false); + } + }; + fetchAllFiles(); + }, [RCInstance, isChannelPrivate, messages, fileToDelete]); const handleOnClose = () => { setFileToDelete({}); @@ -148,6 +169,7 @@ const FileMessage = ({ fileMessage }) => { FileMessage.propTypes = { fileMessage: PropTypes.any.isRequired, + onDeleteFile: PropTypes.func, }; export default memo(FileMessage); diff --git a/packages/react/src/views/Message/Message.js b/packages/react/src/views/Message/Message.js index dd8cf8d09d..c043d52027 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -194,6 +194,7 @@ const Message = ({ message: 'Error in deleting message', }); } + getStarredMessages(); }; const handleEmojiClick = async (e, msg, canReact) => { From 6952cc868408f9883912e90068458818749c1237 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Fri, 14 Mar 2025 15:50:22 +0530 Subject: [PATCH 092/153] fix prettier style --- .../react/src/views/AttachmentPreview/AttachmentPreview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index bf20be3f12..01123dafbe 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -127,7 +127,7 @@ const AttachmentPreview = () => { `} > File name - + { handleFileName(e); @@ -174,7 +174,6 @@ const AttachmentPreview = () => { type="text" css={styles.input} placeholder="Description" - value={description} ref={messageRef} /> From bc8bca2a51b153aa26e1dc86b28b952121a5c810 Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Fri, 14 Mar 2025 15:58:01 +0530 Subject: [PATCH 093/153] CI: deprecated github actions (#981) --- .github/workflows/build-and-lint.yml | 10 +++++----- .github/workflows/build-pr.yml | 6 +++--- .github/workflows/playwright.yml | 26 +++++++++++++------------- .github/workflows/pr-cleanup.yml | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build-and-lint.yml b/.github/workflows/build-and-lint.yml index 375ec8c3af..4d85036e01 100644 --- a/.github/workflows/build-and-lint.yml +++ b/.github/workflows/build-and-lint.yml @@ -12,21 +12,21 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16.19.0 - name: Set up Yarn - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16.19.0 - cache: 'yarn' + cache: "yarn" - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.yarn key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 66c97af32c..b459fd0cc4 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -2,7 +2,7 @@ name: Build PR-Preview on: pull_request_review: - types: submitted + types: [submitted] concurrency: group: ${{github.workflow}}-${{github.ref}} @@ -12,7 +12,7 @@ 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') @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f6f6f20c7c..3d4b133a9e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,45 +13,45 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - + - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: 16.19.0 - + - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.yarn key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - + - name: Install dependencies run: yarn install - + - name: Build the project run: yarn build - + - name: Get installed Playwright version id: playwright-version run: echo "version=$(yarn why --json @playwright/test | grep -h 'workspace:.' | jq --raw-output '.children[].locator' | sed -e 's/@playwright\/test@.*://')" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 + + - uses: actions/cache@v4 id: playwright-cache with: - path: '~/.cache/ms-playwright' - key: '${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}' + path: "~/.cache/ms-playwright" + key: "${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}" restore-keys: | ${{ runner.os }}-playwright- - + - name: Install Playwright's dependencies if: steps.playwright-cache.outputs.cache-hit != 'true' run: npx playwright install --with-deps - + - name: Run Playwright tests run: cd packages/e2e-react && npx playwright test - + - name: Upload Playwright report uses: actions/upload-artifact@v4 if: always() diff --git a/.github/workflows/pr-cleanup.yml b/.github/workflows/pr-cleanup.yml index c5b878373c..6c2af76390 100644 --- a/.github/workflows/pr-cleanup.yml +++ b/.github/workflows/pr-cleanup.yml @@ -9,7 +9,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: gh-deploy From 7ba603a98d2c0aef8ee3d9f51485997f87d6a0ca Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sat, 24 May 2025 12:08:20 +0530 Subject: [PATCH 094/153] fix: correct delete messages permission behavior in Administration and resolve permissions index issue (#767) * fix: correct delete messages permission behavior in Administration * Memoize permissions Mapping * fix: ensure mapping occurs only once on login --------- Co-authored-by: Zishan Ahmad --- packages/api/src/EmbeddedChatApi.ts | 2 +- packages/react/src/hooks/useFetchChatData.js | 103 +++++++++++++++++- packages/react/src/hooks/useRCAuth.js | 16 +-- packages/react/src/store/messageStore.js | 9 ++ packages/react/src/views/ChatBody/ChatBody.js | 13 ++- packages/react/src/views/EmbeddedChat.js | 11 -- packages/react/src/views/Message/Message.js | 15 +++ .../react/src/views/Message/MessageToolbox.js | 27 ++++- 8 files changed, 162 insertions(+), 34 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 44cff0eaf4..2907f0e867 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -741,7 +741,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.delete`, { - body: `{"roomId": "${this.rid}", "msgId": "${msgId}","asUser" : true }`, + body: `{"roomId": "${this.rid}", "msgId": "${msgId}"}`, headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, diff --git a/packages/react/src/hooks/useFetchChatData.js b/packages/react/src/hooks/useFetchChatData.js index 7f5b491252..2078fdf05d 100644 --- a/packages/react/src/hooks/useFetchChatData.js +++ b/packages/react/src/hooks/useFetchChatData.js @@ -1,4 +1,4 @@ -import { useCallback, useContext } from 'react'; +import { useCallback, useContext, useRef, useMemo } from 'react'; import RCContext from '../context/RCInstance'; import { useUserStore, @@ -15,6 +15,7 @@ const useFetchChatData = (showRoles) => { const setMessages = useMessageStore((state) => state.setMessages); const setMessagesOffset = useMessageStore((state) => state.setMessagesOffset); const setAdmins = useMemberStore((state) => state.setAdmins); + const permissionsRef = useRef(null); const setStarredMessages = useStarredMessageStore( (state) => state.setStarredMessages ); @@ -24,6 +25,96 @@ const useFetchChatData = (showRoles) => { const setViewUserInfoPermissions = useUserStore( (state) => state.setViewUserInfoPermissions ); + const setDeleteMessageRoles = useMessageStore( + (state) => state.setDeleteMessageRoles + ); + const setDeleteOwnMessageRoles = useMessageStore( + (state) => state.setDeleteOwnMessageRoles + ); + const setForceDeleteMessageRoles = useMessageStore( + (state) => state.setForceDeleteMessageRoles + ); + const setUserPinPermissions = useUserStore( + (state) => state.setUserPinPermissions + ); + const setEditMessagePermissions = useMessageStore( + (state) => state.setEditMessagePermissions + ); + + const setters = useMemo( + () => + new Map([ + ['viewUserInfo', setViewUserInfoPermissions], + ['deleteMessage', setDeleteMessageRoles], + ['deleteOwnMessage', setDeleteOwnMessageRoles], + ['forceDeleteMessage', setForceDeleteMessageRoles], + ['userPin', setUserPinPermissions], + ['editMessage', setEditMessagePermissions], + ]), + [ + setViewUserInfoPermissions, + setDeleteMessageRoles, + setDeleteOwnMessageRoles, + setForceDeleteMessageRoles, + setUserPinPermissions, + setEditMessagePermissions, + ] + ); + + const permissionKeys = useMemo( + () => + new Map([ + ['viewUserInfo', 'view-full-other-user-info'], + ['deleteMessage', 'delete-message'], + ['deleteOwnMessage', 'delete-own-message'], + ['forceDeleteMessage', 'force-delete-message'], + ['userPin', 'pin-message'], + ['editMessage', 'edit-message'], + ]), + [] + ); + + const applyPermissions = useCallback( + (permissionsMap) => { + Array.from(permissionKeys).forEach(([key, permissionId]) => { + const setter = setters.get(key); + if (setter) { + setter(permissionsMap.get(permissionId)); + } + }); + }, + [permissionKeys, setters] + ); + + const createPermissionsMap = useCallback( + (permissions) => + new Map(permissions.update.map((item) => [item._id, item])), + [] + ); + const fetchAndSetPermissions = useCallback(async () => { + try { + const permissions = await RCInstance.permissionInfo(); + + if ( + !permissionsRef.current || + JSON.stringify(permissions) !== + JSON.stringify(permissionsRef.current.raw) + ) { + const permissionsMap = createPermissionsMap(permissions); + + permissionsRef.current = { + map: permissionsMap, + }; + + applyPermissions(permissionsMap); + } + + return permissionsRef.current.map; + } catch (error) { + console.error('Error fetching permissions:', error); + return null; + } + }, [RCInstance, applyPermissions, createPermissionsMap]); const getMessagesAndRoles = useCallback( async (anonymousMode) => { @@ -73,9 +164,6 @@ const useFetchChatData = (showRoles) => { setMemberRoles(rolesObj); } - - const permissions = await RCInstance.permissionInfo(); - setViewUserInfoPermissions(permissions.update[70]); } catch (e) { console.error(e); } @@ -109,7 +197,12 @@ const useFetchChatData = (showRoles) => { [isUserAuthenticated, RCInstance, setStarredMessages] ); - return { getMessagesAndRoles, getStarredMessages }; + return { + getMessagesAndRoles, + getStarredMessages, + fetchAndSetPermissions, + permissionsRef, + }; }; export default useFetchChatData; diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index c0b1d3a3b4..83b013353b 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -1,12 +1,7 @@ import { useContext } from 'react'; import { useToastBarDispatch } from '@embeddedchat/ui-elements'; import RCContext from '../context/RCInstance'; -import { - useUserStore, - totpModalStore, - useLoginStore, - useMessageStore, -} from '../store'; +import { useUserStore, totpModalStore, useLoginStore } from '../store'; export const useRCAuth = () => { const { RCInstance } = useContext(RCContext); @@ -25,18 +20,11 @@ export const useRCAuth = () => { ); const setPassword = useUserStore((state) => state.setPassword); const setEmailorUser = useUserStore((state) => state.setEmailorUser); - const setUserPinPermissions = useUserStore( - (state) => state.setUserPinPermissions - ); - const setEditMessagePermissions = useMessageStore( - (state) => state.setEditMessagePermissions - ); 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', @@ -68,8 +56,6 @@ export const useRCAuth = () => { setIsTotpModalOpen(false); 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 a9a7265c86..c359a0c8eb 100644 --- a/packages/react/src/store/messageStore.js +++ b/packages/react/src/store/messageStore.js @@ -10,6 +10,9 @@ const useMessageStore = create((set, get) => ({ editMessage: {}, messagesOffset: 0, quoteMessage: [], + deleteMessageRoles: {}, + deleteOwnMessageRoles: {}, + forceDeleteMessageRoles: {}, messageToReport: NaN, showReportMessage: false, isRecordingMessage: false, @@ -121,6 +124,12 @@ const useMessageStore = create((set, get) => ({ threadMessages: [], })); }, + setDeleteMessageRoles: (deleteMessageRoles) => + set((state) => ({ ...state, deleteMessageRoles })), + setDeleteOwnMessageRoles: (deleteOwnMessageRoles) => + set((state) => ({ ...state, deleteOwnMessageRoles })), + setForceDeleteMessageRoles: (forceDeleteMessageRoles) => + set((state) => ({ ...state, forceDeleteMessageRoles })), setThreadMessages: (messages) => set(() => ({ threadMessages: messages })), setHeaderTitle: (title) => set(() => ({ headerTitle: title })), })); diff --git a/packages/react/src/views/ChatBody/ChatBody.js b/packages/react/src/views/ChatBody/ChatBody.js index 4a36e2aa96..eeae34dfb7 100644 --- a/packages/react/src/views/ChatBody/ChatBody.js +++ b/packages/react/src/views/ChatBody/ChatBody.js @@ -85,7 +85,8 @@ const ChatBody = ({ const username = useUserStore((state) => state.username); - const { getMessagesAndRoles } = useFetchChatData(showRoles); + const { getMessagesAndRoles, fetchAndSetPermissions, permissionsRef } = + useFetchChatData(showRoles); const getThreadMessages = useCallback(async () => { if (isUserAuthenticated && threadMainMessage?._id) { @@ -166,6 +167,16 @@ const ChatBody = ({ }); }, [RCInstance, anonymousMode, getMessagesAndRoles]); + useEffect(() => { + RCInstance.auth.onAuthChange((user) => { + if (user) { + fetchAndSetPermissions(); + } else { + permissionsRef.current = null; + } + }); + }, []); + const handlePopupClick = () => { scrollToBottom(); setIsUserScrolledUp(false); diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index 17b1ff1a41..f3b94c7b48 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -66,7 +66,6 @@ const EmbeddedChat = (props) => { const [isSynced, setIsSynced] = useState(!remoteOpt); const { getToken, saveToken, deleteToken } = getTokenStorage(secure); const { - isUserAuthenticated, setIsUserAuthenticated, setUsername: setAuthenticatedUsername, setUserAvatarUrl: setAuthenticatedAvatarUrl, @@ -84,13 +83,6 @@ const EmbeddedChat = (props) => { })); const setIsLoginIn = useLoginStore((state) => state.setIsLoginIn); - const setUserPinPermissions = useUserStore( - (state) => state.setUserPinPermissions - ); - - const setEditMessagePermissions = useMessageStore( - (state) => state.setEditMessagePermissions - ); if (isClosable && !setClosableState) { throw Error( 'Please provide a setClosableState to props when isClosable = true' @@ -132,9 +124,6 @@ const EmbeddedChat = (props) => { setIsLoginIn(true); try { 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 c043d52027..355cde9b4a 100644 --- a/packages/react/src/views/Message/Message.js +++ b/packages/react/src/views/Message/Message.js @@ -72,6 +72,15 @@ const Message = ({ editMessage: state.editMessage, setEditMessage: state.setEditMessage, })); + const deleteMessagePermissions = useMessageStore( + (state) => state.deleteMessageRoles.roles + ); + const deleteOwnMessagePermissions = useMessageStore( + (state) => state.deleteOwnMessageRoles.roles + ); + const forceDeleteMessagePermissions = useMessageStore( + (state) => state.forceDeleteMessageRoles.roles + ); const isMe = message.u._id === authenticatedUserId; @@ -94,6 +103,9 @@ const Message = ({ const bubbleStyles = useBubbleStyles(isMe); const pinRoles = new Set(pinPermissions); const editMessageRoles = new Set(editMessagePermissions); + const deleteMessageRoles = new Set(deleteMessagePermissions); + const deleteOwnMessageRoles = new Set(deleteOwnMessagePermissions); + const forceDeleteMessageRoles = new Set(forceDeleteMessagePermissions); const variantStyles = !isInSidebar && variantOverrides === 'bubble' ? bubbleStyles : {}; @@ -291,6 +303,9 @@ const Message = ({ authenticatedUserId={authenticatedUserId} userRoles={userRoles} pinRoles={pinRoles} + deleteMessageRoles={deleteMessageRoles} + deleteOwnMessageRoles={deleteOwnMessageRoles} + forceDeleteMessageRoles={forceDeleteMessageRoles} editMessageRoles={editMessageRoles} handleCopyMessage={handleCopyMessage} handleCopyMessageLink={handleCopyMessageLink} diff --git a/packages/react/src/views/Message/MessageToolbox.js b/packages/react/src/views/Message/MessageToolbox.js index 0208ff663d..0749eed697 100644 --- a/packages/react/src/views/Message/MessageToolbox.js +++ b/packages/react/src/views/Message/MessageToolbox.js @@ -25,6 +25,9 @@ export const MessageToolbox = ({ authenticatedUserId, userRoles, pinRoles, + deleteMessageRoles, + deleteOwnMessageRoles, + forceDeleteMessageRoles, editMessageRoles, handleOpenThread, handleEmojiClick, @@ -86,6 +89,28 @@ export const MessageToolbox = ({ ? true : message.u._id === authenticatedUserId; + const isAllowedToDeleteMessage = userRoles.some((role) => + deleteMessageRoles.has(role) + ); + const isAllowedToDeleteOwnMessage = userRoles.some((role) => + deleteOwnMessageRoles.has(role) + ); + const isAllowedToForceDeleteMessage = userRoles.some((role) => + forceDeleteMessageRoles.has(role) + ); + + const isVisibleForMessageType = + message.files?.[0].type !== 'audio/mpeg' && + message.files?.[0].type !== 'video/mp4'; + + const canDeleteMessage = isAllowedToForceDeleteMessage + ? true + : isAllowedToDeleteMessage + ? true + : isAllowedToDeleteOwnMessage + ? message.u._id === authenticatedUserId + : false; + const options = useMemo( () => ({ reply: { @@ -159,7 +184,7 @@ export const MessageToolbox = ({ id: 'delete', onClick: () => setShowDeleteModal(true), iconName: 'trash', - visible: message.u._id === authenticatedUserId, + visible: canDeleteMessage, type: 'destructive', }, report: { From f555531c0292b2f3797d3b19efc02a67ce101242 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Sat, 24 May 2025 12:10:29 +0530 Subject: [PATCH 095/153] Fix: Highlight entire message body for 'Jump to Message' functionality (#822) * Fix: Highlight entire message body for 'Jump to Message' functionality and improve visibility in dark mode * Changed to Subtle shade * Syncing same color config for thread message case * Made Dark mode color subtle --- .../common/MessageAggregator.js | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js index 44461790a3..ab8c3bc2f0 100644 --- a/packages/react/src/views/MessageAggregators/common/MessageAggregator.js +++ b/packages/react/src/views/MessageAggregators/common/MessageAggregator.js @@ -1,5 +1,5 @@ import React, { useState, useMemo } from 'react'; -import { isSameDay, format, set } from 'date-fns'; +import { isSameDay, format } from 'date-fns'; import { Box, Sidebar, @@ -7,6 +7,8 @@ import { useTheme, ActionButton, Icon, + lighten, + darken, } from '@embeddedchat/ui-elements'; import { MessageDivider } from '../../Message/MessageDivider'; import Message from '../../Message/Message'; @@ -33,6 +35,7 @@ export const MessageAggregator = ({ viewType = 'Sidebar', }) => { const { theme } = useTheme(); + const { mode } = useTheme(); const styles = getMessageAggregatorStyles(theme); const setExclusiveState = useSetExclusiveState(); const { ECOptions } = useRCContext(); @@ -59,42 +62,66 @@ export const MessageAggregator = ({ console.error('Invalid message object:', msg); return; } + const { _id: msgId, tmid: threadId } = msg; + if (msgId) { let element; if (threadId) { const parentMessage = messages.find((m) => m._id === threadId); + if (parentMessage) { closeThread(); + setTimeout(() => { openThread(parentMessage); setShowSidebar(false); + setTimeout(() => { - element = document.getElementById(`ec-message-body-${msgId}`); + const childElement = document.getElementById( + `ec-message-body-${msgId}` + ); + element = childElement.closest('.ec-message'); + if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'nearest', }); - element.style.backgroundColor = theme.colors.warning; + + element.style.backgroundColor = + mode === 'light' + ? lighten(theme.colors.warning, 0.85) + : darken(theme.colors.warningForeground, 0.75); + setTimeout(() => { element.style.backgroundColor = ''; - }, 1000); + }, 2000); } }, 300); }, 300); } } else { closeThread(); + setTimeout(() => { - element = document.getElementById(`ec-message-body-${msgId}`); + const childElement = document.getElementById( + `ec-message-body-${msgId}` + ); + element = childElement.closest('.ec-message'); + if (element) { setShowSidebar(false); element.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - element.style.backgroundColor = theme.colors.warning; + + element.style.backgroundColor = + mode === 'light' + ? lighten(theme.colors.warning, 0.85) + : darken(theme.colors.warningForeground, 0.75); + setTimeout(() => { element.style.backgroundColor = ''; - }, 1000); + }, 2000); } }, 300); } From 04acec796ed23d979bf470677d6b0b419f837a0a Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sat, 24 May 2025 12:13:53 +0530 Subject: [PATCH 096/153] fix: resolve mention highlighting issue for "application" and "text" file types and improve design (#836) * fix: resolve mention highlighting issue for "application" and "text" file types and improve design * fix: ensure mentioned users in file description appear in mentions list * fix: ensure user mentions in files are not gloabalized * fix download icon alignment and quoting issues --------- Co-authored-by: Zishan Ahmad --- .../src/views/AttachmentHandler/Attachment.js | 32 +- .../AttachmentHandler/AttachmentMetadata.js | 195 +++++------ .../views/AttachmentHandler/TextAttachment.js | 302 +++++++++++++----- .../Message/BubbleVariant/Bubble.styles.js | 5 + .../MessageAggregators/MentionedMessages.js | 20 +- .../src/views/QuoteMessage/QuoteMessage.js | 14 + .../src/components/Button/Button.styles.js | 7 +- 7 files changed, 350 insertions(+), 225 deletions(-) diff --git a/packages/react/src/views/AttachmentHandler/Attachment.js b/packages/react/src/views/AttachmentHandler/Attachment.js index ec752e9bcd..20c329138d 100644 --- a/packages/react/src/views/AttachmentHandler/Attachment.js +++ b/packages/react/src/views/AttachmentHandler/Attachment.js @@ -1,11 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { css } from '@emotion/react'; -import { Box, Icon } from '@embeddedchat/ui-elements'; import ImageAttachment from './ImageAttachment'; import AudioAttachment from './AudioAttachment'; import VideoAttachment from './VideoAttachment'; -import TextAttachment from './TextAttachment'; +import FileAttachment from './TextAttachment'; const Attachment = ({ attachment, host, type, variantStyles = {}, msg }) => { const author = { @@ -45,16 +43,6 @@ const Attachment = ({ attachment, host, type, variantStyles = {}, msg }) => { /> ); } - if (attachment && attachment.text) { - return ( - - ); - } if ( attachment.attachments && Array.isArray(attachment.attachments) && @@ -101,16 +89,14 @@ const Attachment = ({ attachment, host, type, variantStyles = {}, msg }) => { ); } return ( - - {attachment?.description} - - - {attachment.title} - + ); }; diff --git a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js index 17e25c03db..0c4eda27aa 100644 --- a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js +++ b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js @@ -18,7 +18,7 @@ const AttachmentMetadata = ({ const downloadUrl = URL.createObjectURL(data); const anchor = document.createElement('a'); anchor.href = downloadUrl; - anchor.download = attachment.title || 'download'; + anchor.download = attachment?.title || 'download'; document.body.appendChild(anchor); anchor.click(); @@ -29,6 +29,25 @@ const AttachmentMetadata = ({ } }; + const getFormattedFileSize = () => { + let sizeInBytes; + + if (attachment?.image_type && attachment?.image_size) { + sizeInBytes = attachment.image_size; + } else if (attachment?.video_type && attachment?.video_size) { + sizeInBytes = attachment.video_size; + } else if (attachment?.audio_type && attachment?.audio_size) { + sizeInBytes = attachment.audio_size; + } else if (attachment?.size) { + sizeInBytes = attachment.size; + } else { + sizeInBytes = 0; + } + + const sizeInKB = (sizeInBytes / 1024).toFixed(2); + return `${sizeInKB} kB`; + }; + return ( -
- {msg ? ( - - ) : ( - attachment.description - )} -
+ {attachment?.description && ( +
+ {msg ? ( + + ) : ( + attachment?.description + )} +
+ )} + - +

- {attachment.title.length > 22 + {attachment?.title?.length > 22 ? `${attachment.title.substring(0, 22)}...` - : attachment.title} + : attachment?.title}

- ( - {attachment.image_size - ? (attachment.image_size / 1024).toFixed(2) - : 0}{' '} - kB) + ({getFormattedFileSize()})
+ - - - { - onExpandCollapseClick(); - }} - /> - - - - - - - + + + + + +
diff --git a/packages/react/src/views/AttachmentHandler/TextAttachment.js b/packages/react/src/views/AttachmentHandler/TextAttachment.js index b8477cd8bb..423684e4f1 100644 --- a/packages/react/src/views/AttachmentHandler/TextAttachment.js +++ b/packages/react/src/views/AttachmentHandler/TextAttachment.js @@ -1,82 +1,174 @@ -import React, { useContext } from 'react'; +import React, { useState, useContext } from 'react'; import { css } from '@emotion/react'; import PropTypes from 'prop-types'; -import { Box, Avatar, useTheme } from '@embeddedchat/ui-elements'; +import { Box, Avatar, useTheme, Icon } from '@embeddedchat/ui-elements'; +import AttachmentMetadata from './AttachmentMetadata'; import RCContext from '../../context/RCInstance'; import { Markdown } from '../Markdown'; -const TextAttachment = ({ attachment, type, variantStyles = {} }) => { +const FileAttachment = ({ + attachment, + host, + type, + author, + variantStyles = {}, + msg, +}) => { const { RCInstance } = useContext(RCContext); - const getUserAvatarUrl = (authorIcon) => { - const host = RCInstance.getHost(); - const URL = `${host}${authorIcon}`; - return URL; + const { theme } = useTheme(); + const [isExpanded, setIsExpanded] = useState(true); + + const getUserAvatarUrl = (icon) => { + const instanceHost = RCInstance.getHost(); + return `${instanceHost}${icon}`; }; - const { theme } = useTheme(); + const toggleExpanded = () => { + setIsExpanded((prevState) => !prevState); + }; + + const formatFileSize = (bytes) => { + if (!bytes || bytes === 0) return '0 B'; + + const units = ['B', 'KB', 'MB', 'GB']; + const k = 1024; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + const unitIndex = Math.min(i, units.length - 1); + + const size = bytes / k ** unitIndex; + const decimals = unitIndex === 0 ? 0 : unitIndex === 1 ? 1 : 2; + + return `${size.toFixed(decimals)} ${units[unitIndex]}`; + }; + + const getFileSizeWithFormat = (size, format) => { + const formattedSize = formatFileSize(size); + return format ? `${formattedSize} - ${format}` : formattedSize; + }; return ( - + {attachment?.author_name && ( - <> + @{attachment?.author_name} - + )} - - - {attachment?.text ? ( - attachment.text[0] === '[' ? ( - attachment.text.match(/\n(.*)/)?.[1] || '' - ) : ( - - ) - ) : ( - '' + + {!attachment?.text && !attachment?.attachments && ( + + )} + {isExpanded && (attachment?.title_link || attachment?.text) && ( + + {attachment?.text ? ( + attachment.text[0] === '[' ? ( + attachment.text.match(/\n(.*)/)?.[1] || '' + ) : ( + + ) + ) : !attachment.attachments ? ( + + + + + {attachment.title} + + + {getFileSizeWithFormat(attachment.size, attachment.format)} + + + + ) : ( + '' + )} + )} {attachment?.attachments && + Array.isArray(attachment.attachments) && attachment.attachments.map((nestedAttachment, index) => ( { gap: 0.3rem; align-items: center; `, - variantStyles.textUserInfo, ]} > @@ -128,26 +219,82 @@ const TextAttachment = ({ attachment, type, variantStyles = {} }) => { )} - - {nestedAttachment?.text ? ( - nestedAttachment.text[0] === '[' ? ( - nestedAttachment.text.match(/\n(.*)/)?.[1] || '' + + + + {isExpanded && ( + + {nestedAttachment?.text ? ( + nestedAttachment.text[0] === '[' ? ( + nestedAttachment.text.match(/\n(.*)/)?.[1] || '' + ) : ( + + ) ) : ( - - ) - ) : ( - '' - )} - + + + + + {nestedAttachment?.title} + + + {getFileSizeWithFormat( + nestedAttachment?.size, + nestedAttachment?.format + )} + + + + )} + + )} ))} @@ -155,8 +302,13 @@ const TextAttachment = ({ attachment, type, variantStyles = {} }) => { ); }; -export default TextAttachment; +export default FileAttachment; -TextAttachment.propTypes = { +FileAttachment.propTypes = { attachment: PropTypes.object, + host: PropTypes.string, + type: PropTypes.string, + author: PropTypes.object, + variantStyles: PropTypes.object, + msg: PropTypes.object, }; diff --git a/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js b/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js index 5f87c0b5e4..3138692604 100644 --- a/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js +++ b/packages/react/src/views/Message/BubbleVariant/Bubble.styles.js @@ -93,6 +93,11 @@ export const getBubbleStyles = (theme) => { border-radius: inherit; overflow: hidden; `, + fileAttachmentContainer: css` + border: 1px solid ${theme.colors.border}; + border-radius: inherit; + overflow: hidden; + `, pinnedContainer: css` max-width: 100%; `, diff --git a/packages/react/src/views/MessageAggregators/MentionedMessages.js b/packages/react/src/views/MessageAggregators/MentionedMessages.js index 67da546a5b..742405a4c9 100644 --- a/packages/react/src/views/MessageAggregators/MentionedMessages.js +++ b/packages/react/src/views/MessageAggregators/MentionedMessages.js @@ -5,16 +5,32 @@ import { MessageAggregator } from './common/MessageAggregator'; const MentionedMessages = () => { const authenticatedUserId = useUserStore((state) => state.userId); + const username = useUserStore((state) => state.username); const { variantOverrides } = useComponentOverrides('MentionedMessages'); const viewType = variantOverrides.viewType || 'Sidebar'; + const hasMention = (msg) => { + if (!msg.attachments?.length) { + return false; + } + + return msg.attachments.some((attachment) => + attachment.descriptionMd?.some((desc) => + desc.value?.some( + (val) => val.type === 'MENTION_USER' && val.value.value === username + ) + ) + ); + }; + return ( - msg.mentions && - msg.mentions.some((star) => star._id === authenticatedUserId) + (msg.mentions && + msg.mentions.some((star) => star._id === authenticatedUserId)) || + hasMention(msg) } viewType={viewType} /> diff --git a/packages/react/src/views/QuoteMessage/QuoteMessage.js b/packages/react/src/views/QuoteMessage/QuoteMessage.js index 7fd9634e04..989c4887a8 100644 --- a/packages/react/src/views/QuoteMessage/QuoteMessage.js +++ b/packages/react/src/views/QuoteMessage/QuoteMessage.js @@ -12,6 +12,7 @@ import RCContext from '../../context/RCInstance'; import { useMessageStore } from '../../store'; import getQuoteMessageStyles from './QuoteMessage.styles'; import Attachment from '../AttachmentHandler/Attachment'; +import FileAttachment from '../AttachmentHandler/TextAttachment'; import { Markdown } from '../Markdown'; const QuoteMessage = ({ className = '', style = {}, message }) => { @@ -22,6 +23,10 @@ const QuoteMessage = ({ className = '', style = {}, message }) => { const URL = `${host}/avatar/${username}`; return URL; }; + const author = { + authorIcon: message?.author_icon, + authorName: message?.author_name, + }; const { theme } = useTheme(); const styles = getQuoteMessageStyles(theme); const removeQuoteMessage = useMessageStore( @@ -82,6 +87,15 @@ const QuoteMessage = ({ className = '', style = {}, message }) => { /> Your browser does not support the audio element. + ) : message.file.type.startsWith('application/') || + message.file.type.startsWith('text/') ? ( + ) : ( {message.msg ? ( diff --git a/packages/ui-elements/src/components/Button/Button.styles.js b/packages/ui-elements/src/components/Button/Button.styles.js index fac0bced14..38b54ea21a 100644 --- a/packages/ui-elements/src/components/Button/Button.styles.js +++ b/packages/ui-elements/src/components/Button/Button.styles.js @@ -14,7 +14,9 @@ const getButtonStyles = (theme) => { const styles = { main: (type, size) => css` cursor: pointer; - display: inline-block; + display: inline-flex; + justify-content: center; + align-items: center; background-color: ${theme.colors[type] || 'currentColor'}; color: ${theme.colors[`${type}Foreground`] || 'currentColor'}; border: none; @@ -63,9 +65,6 @@ const getButtonStyles = (theme) => { min-width: ${getSquareSize(size)}; height: ${getSquareSize(size)}; padding: 0; - display: flex; - justify-content: center; - align-items: center; flex-shrink: 0; } From 6b6340ac1ccabb70d43390ef89442cccd3713def Mon Sep 17 00:00:00 2001 From: Anirban Singha <143536290+SinghaAnirban005@users.noreply.github.com> Date: Sat, 24 May 2025 12:14:58 +0530 Subject: [PATCH 097/153] fix: correct UI for archived channels and display channel topic (#905) * fix: correct UI for archived channels and display channel topic * add appropriate border color to archived box --- packages/api/src/EmbeddedChatApi.ts | 35 +++++++++++ packages/react/src/store/channelStore.js | 3 + .../react/src/views/ChatHeader/ChatHeader.js | 16 ++++- .../src/views/ChatHeader/ChatHeader.styles.js | 15 +++++ .../react/src/views/ChatInput/ChatInput.js | 62 ++++++++++++------- .../views/RoomInformation/RoomInformation.js | 39 ++++++++---- .../RoomInformation/RoomInformation.styles.js | 18 +++++- 7 files changed, 151 insertions(+), 37 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 2907f0e867..46690e24e6 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -485,6 +485,41 @@ export default class EmbeddedChatApi { } } + async getRoomInfo() { + try { + const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; + const response = await fetch( + `${this.host}/api/v1/method.call/rooms%3Aget`, + { + body: JSON.stringify({ + message: JSON.stringify({ + msg: "method", + id: null, + method: "rooms/get", + params: [], + }), + }), + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + method: "POST", + } + ); + + const result = await response.json(); + + if (result.success && result.message) { + const parsedMessage = JSON.parse(result.message); + return parsedMessage; + } + return null; + } catch (err) { + console.error(err); + } + } + async permissionInfo() { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; diff --git a/packages/react/src/store/channelStore.js b/packages/react/src/store/channelStore.js index f3acbb914f..21d036c52a 100644 --- a/packages/react/src/store/channelStore.js +++ b/packages/react/src/store/channelStore.js @@ -4,11 +4,14 @@ const useChannelStore = create((set) => ({ showChannelinfo: false, isChannelPrivate: false, isChannelReadOnly: false, + isChannelArchived: false, isRoomTeam: false, setShowChannelinfo: (showChannelinfo) => set(() => ({ showChannelinfo })), channelInfo: {}, setChannelInfo: (channelInfo) => set(() => ({ channelInfo })), setIsChannelPrivate: (isChannelPrivate) => set(() => ({ isChannelPrivate })), + setIsChannelArchived: (isChannelArchived) => + set(() => ({ isChannelArchived })), setIsRoomTeam: (isRoomTeam) => set(() => ({ isRoomTeam })), setIsChannelReadOnly: (isChannelReadOnly) => set(() => ({ isChannelReadOnly })), diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index cf7457a9eb..9143598d30 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -73,6 +73,9 @@ const ChatHeader = ({ const setIsChannelPrivate = useChannelStore( (state) => state.setIsChannelPrivate ); + const setIsChannelArchived = useChannelStore( + (state) => state.setIsChannelArchived + ); const isRoomTeam = useChannelStore((state) => state.isRoomTeam); const setIsRoomTeam = useChannelStore((state) => state.setIsRoomTeam); const setIsChannelReadOnly = useChannelStore( @@ -130,7 +133,6 @@ const ChatHeader = ({ }; const setCanSendMsg = useUserStore((state) => state.setCanSendMsg); const authenticatedUserId = useUserStore((state) => state.userId); - const handleLogout = useCallback(async () => { try { await RCInstance.logout(); @@ -190,6 +192,14 @@ const ChatHeader = ({ message: "Channel doesn't exist. Logging out.", }); await RCInstance.logout(); + } else if ( + 'errorType' in res && + res.errorType === 'error-room-archived' + ) { + setIsChannelArchived(true); + const roomInfo = await RCInstance.getRoomInfo(); + const roomData = roomInfo.result[roomInfo.result.length - 1]; + setChannelInfo(roomData); } else if ('errorType' in res && res.errorType === 'Not Allowed') { dispatchToastMessage({ type: 'error', @@ -369,9 +379,9 @@ const ChatHeader = ({ - + setExclusiveState(setShowChannelinfo)} diff --git a/packages/react/src/views/ChatHeader/ChatHeader.styles.js b/packages/react/src/views/ChatHeader/ChatHeader.styles.js index 54770d8a54..46a9ecd32c 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.styles.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.styles.js @@ -32,8 +32,17 @@ const getChatHeaderStyles = ({ theme, mode }) => { box-shadow: ${theme.shadows[1]}; `, + channelInfoContainer: css` + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; + `, + channelDescription: css` ${rowCentreAlign} + flex: 1; + min-width: 0; gap: 0.5rem; `, @@ -50,7 +59,13 @@ const getChatHeaderStyles = ({ theme, mode }) => { `, channelTopic: css` opacity: 0.8rem; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + word-break: break-word; + overflow: hidden; font-size: 1rem; + text-overflow: ellipsis; `, }; return styles; diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index e04fd70b13..c7d41f2059 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -74,13 +74,17 @@ const ChatInput = ({ scrollToBottom }) => { name: state.name, })); - const { isChannelPrivate, isChannelReadOnly, channelInfo } = useChannelStore( - (state) => ({ - isChannelPrivate: state.isChannelPrivate, - isChannelReadOnly: state.isChannelReadOnly, - channelInfo: state.channelInfo, - }) - ); + const { + isChannelPrivate, + isChannelReadOnly, + channelInfo, + isChannelArchived, + } = useChannelStore((state) => ({ + isChannelPrivate: state.isChannelPrivate, + isChannelReadOnly: state.isChannelReadOnly, + channelInfo: state.channelInfo, + isChannelArchived: state.isChannelArchived, + })); const { members, setMembersHandler } = useMemberStore((state) => ({ members: state.members, @@ -540,15 +544,27 @@ const ChatInput = ({ scrollToBottom }) => { { sendTypingStop(); @@ -566,14 +582,16 @@ const ChatInput = ({ scrollToBottom }) => { `} > {isUserAuthenticated ? ( - sendMessage()} - type="primary" - disabled={disableButton || isRecordingMessage} - icon="send" - /> + !isChannelArchived ? ( + sendMessage()} + type="primary" + disabled={disableButton || isRecordingMessage} + icon="send" + /> + ) : null ) : ( - - + + + + {messageDescription(message)} + + {children} + + + + + + + + ); }; From 710305e49903cf3b61150532ba04005a04b00b76 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sat, 31 May 2025 14:03:07 +0530 Subject: [PATCH 104/153] fixed blog broken links --- packages/docs/blog/EmbeddedChat-2024.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docs/blog/EmbeddedChat-2024.md b/packages/docs/blog/EmbeddedChat-2024.md index 6827d5940b..2d65e99fb0 100644 --- a/packages/docs/blog/EmbeddedChat-2024.md +++ b/packages/docs/blog/EmbeddedChat-2024.md @@ -89,7 +89,7 @@ A demonstration video is available here: -For further details on theming, you can visit the [documentation](https://github.com/RocketChat/EmbeddedChat/blob/develop/packages/docs/theming.md) or check out the [technical guide](https://github.com/RocketChat/EmbeddedChat/blob/develop/packages/docs/theming_technical.md) for insights on how theming is implemented in the repository. +For further details on theming, you can visit the [documentation](https://rocketchat.github.io/EmbeddedChat/docs/docs/Usage/theming) or check out the [technical guide](https://rocketchat.github.io/EmbeddedChat/docs/docs/Development/theming_technical) for insights on how theming is implemented in the repository. ### Enhanced Authentication with HTTP-Only Cookies @@ -99,7 +99,7 @@ A video demonstration is available here: -For more information on authentication, refer to the [authentication guide](https://github.com/RocketChat/EmbeddedChat/blob/develop/packages/docs/authentication.md). +For more information on authentication, refer to the [authentication guide](https://rocketchat.github.io/EmbeddedChat/docs/docs/Usage/authentication). ### UI-Kit Improvement @@ -134,7 +134,7 @@ The following videos demonstrate its usage: -To set up the EmbeddedChat RC App, follow this guide: [EmbeddedChat RC App Setup](https://github.com/RocketChat/EmbeddedChat/blob/develop/packages/docs/ec_rc_setup.md). +To set up the EmbeddedChat RC App, follow this guide: [EmbeddedChat RC App Setup](https://rocketchat.github.io/EmbeddedChat/docs/docs/Usage/ec_rc_setup). ### Layout Editor @@ -144,7 +144,7 @@ A video demonstration showcases the features: -To learn more about the layout editor, visit the guide: [Layout Editor Guide](https://github.com/RocketChat/EmbeddedChat/blob/develop/packages/docs/layout_editor.md). +To learn more about the layout editor, visit the guide: [Layout Editor Guide](https://rocketchat.github.io/EmbeddedChat/docs/docs/Usage/layout_editor). ## 🚀 Contributions From 70d714c667a05a2ebb301cc646461a95e7589ff2 Mon Sep 17 00:00:00 2001 From: Khizar Shah <109973520+Khizarshah01@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:00:22 +0530 Subject: [PATCH 105/153] fix: auto-detect OS/browser color theme on initial load (#1005) --- packages/docs/docusaurus.config.js | 5 +++++ packages/docs/src/css/custom.css | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/docs/docusaurus.config.js b/packages/docs/docusaurus.config.js index 1201dae919..0ffbd70998 100644 --- a/packages/docs/docusaurus.config.js +++ b/packages/docs/docusaurus.config.js @@ -123,6 +123,11 @@ const config = { }, ], }, + colorMode: { + defaultMode: 'light', + disableSwitch: false, + respectPrefersColorScheme: true, + }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, diff --git a/packages/docs/src/css/custom.css b/packages/docs/src/css/custom.css index 8fa55b551b..8697856532 100644 --- a/packages/docs/src/css/custom.css +++ b/packages/docs/src/css/custom.css @@ -5,7 +5,7 @@ */ /* You can override the default Infima variables here. */ -:root { +[data-theme='light']:root { --ifm-color-primary: #1f3e05; --ifm-color-primary-dark: #29784c; --ifm-color-primary-darker: #277148; From fbf5daedc367d69d229f8e44a066dd6a5c65acb8 Mon Sep 17 00:00:00 2001 From: Khizar Shah <109973520+Khizarshah01@users.noreply.github.com> Date: Sun, 8 Jun 2025 16:53:10 +0530 Subject: [PATCH 106/153] docs: add Node.js >=18 requirement and nvm instructions for docs/ folder & format the readme (#1007) --- packages/docs/README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/docs/README.md b/packages/docs/README.md index 812eeb9d01..aaf7803b93 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -1,17 +1,29 @@ # EmbeddedChat Documentation -This is the official documentation website of EmbeddedChat +This is the official documentation website of EmbeddedChat. + +> **Node.js Version Requirement** +> +> The `docs/` folder requires **Node.js v18 or higher** to run correctly. +> If you’re using a lower version (e.g., v16.19.0 from other parts of the monorepo), you may encounter errors. +> +> Use [NVM](https://github.com/nvm-sh/nvm) to install and switch to the correct version: +> +> ```bash +> nvm install 18 +> nvm use 18 +> ``` ### Installation ``` -$ yarn install + yarn install ``` ### Local Development ``` -$ yarn dev + yarn dev ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. @@ -19,7 +31,7 @@ This command starts a local development server and opens up a browser window. Mo ### Build ``` -$ yarn build + yarn build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. From 58e1f816c8498d57cc5fd9ef5e6538941586950f Mon Sep 17 00:00:00 2001 From: Khizar Shah <109973520+Khizarshah01@users.noreply.github.com> Date: Sun, 21 Sep 2025 22:10:00 +0530 Subject: [PATCH 107/153] fix: implement URL validation in InsertLinkToolBox (#1012) --- package.json | 3 +- .../src/views/ChatInput/InsertLinkToolBox.js | 39 ++++++++++++++++--- yarn.lock | 8 ++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3868bafb9c..c589243fe2 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "typescript": "^5.1.3" }, "dependencies": { - "dompurify": "^3.1.6" + "dompurify": "^3.1.6", + "validator": "^13.15.15" } } diff --git a/packages/react/src/views/ChatInput/InsertLinkToolBox.js b/packages/react/src/views/ChatInput/InsertLinkToolBox.js index 49e8e0d4ad..c8f9247628 100644 --- a/packages/react/src/views/ChatInput/InsertLinkToolBox.js +++ b/packages/react/src/views/ChatInput/InsertLinkToolBox.js @@ -1,5 +1,6 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Modal, Input, Button, useTheme } from '@embeddedchat/ui-elements'; +import validator from 'validator'; import { getInsertLinkModalStyles } from './ChatInput.styles'; const InsertLinkToolBox = ({ @@ -9,14 +10,35 @@ const InsertLinkToolBox = ({ }) => { const { theme } = useTheme(); const styles = getInsertLinkModalStyles(theme); - const [linkText, setLinkText] = useState(selectedText || 'Text'); - const [linkUrl, setLinkUrl] = useState(null); + const [linkText, setLinkText] = useState(selectedText || ''); + const [linkUrl, setLinkUrl] = useState(''); + const [isUrlValid, setIsUrlValid] = useState(false); + + const validateUrl = (url) => + validator.isURL(url, { + protocols: ['http', 'https'], + require_protocol: true, + require_valid_protocol: true, + disallow_auth: true, + }); + + useEffect(() => { + const isValid = validateUrl(linkUrl); + setIsUrlValid(isValid); + }, [linkUrl]); const handleLinkTextOnChange = (e) => { setLinkText(e.target.value); }; const handleLinkUrlOnChange = (e) => { - setLinkUrl(e.target.value); + const url = e.target.value.trim(); + setLinkUrl(url); + setIsUrlValid(url ? validateUrl(url) : false); + }; + + const handleAdd = () => { + if (!isUrlValid) return; + handleAddLink(linkText, linkUrl); }; return ( @@ -30,11 +52,13 @@ const InsertLinkToolBox = ({ type="text" onChange={handleLinkTextOnChange} value={linkText} + placeholder="Text" css={styles.inputWithFormattingBox} /> @@ -43,7 +67,12 @@ const InsertLinkToolBox = ({ - diff --git a/yarn.lock b/yarn.lock index d3dec11bea..23b67cfd11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15209,6 +15209,7 @@ __metadata: husky: ^9.0.11 lerna: ^6.6.2 typescript: ^5.1.3 + validator: ^13.15.15 languageName: unknown linkType: soft @@ -31653,6 +31654,13 @@ __metadata: languageName: node linkType: hard +"validator@npm:^13.15.15": + version: 13.15.15 + resolution: "validator@npm:13.15.15" + checksum: 10c1b9215a25c31497c481cf4a3ee3e17dcf0b8a219445788e7167ed1b93b8597bbf657bd5c8ca22199b699c3ab41df902c23526cc514075c4b71bd19a13d02c + languageName: node + linkType: hard + "vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" From 23b6c398e56323ba858592b5a7f332401c03b910 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sun, 19 Oct 2025 18:21:56 +0530 Subject: [PATCH 108/153] removed placeholder lines Signed-off-by: Zishan Ahmad --- packages/docs/docs/Usage/authentication.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/docs/docs/Usage/authentication.md b/packages/docs/docs/Usage/authentication.md index 95c3c3e7b9..3a496980cc 100644 --- a/packages/docs/docs/Usage/authentication.md +++ b/packages/docs/docs/Usage/authentication.md @@ -74,8 +74,6 @@ This method leverages the OAuth configuration established in Rocket.Chat, ensuri For instructions on installing the EmbeddedChat RC app on your Rocket.Chat server, refer to the [EmbeddedChat RC App installation guide](../Usage/ec_rc_setup.md). -Certainly! Here are the instructions to enable OAuth login in the EmbeddedChat RC app, without using sub-bullets: - #### Steps to Enable OAuth Login in EmbeddedChat RC App 1. Copy Callback URL: From 698489748fa0083e8938efbd41c51205bc6a02d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=99=E4=BB=80=E7=93=A6=E7=89=B9?= <114943221+ShashwatPS@users.noreply.github.com> Date: Thu, 27 Nov 2025 20:57:42 +0530 Subject: [PATCH 109/153] fixed userSidibar re-render (#1014) --- packages/react/src/views/UserInformation/UserInformation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/views/UserInformation/UserInformation.js b/packages/react/src/views/UserInformation/UserInformation.js index d03232d651..de79395c50 100644 --- a/packages/react/src/views/UserInformation/UserInformation.js +++ b/packages/react/src/views/UserInformation/UserInformation.js @@ -58,7 +58,7 @@ const UserInformation = () => { }; getCurrentUserInfo(); - }, [RCInstance, setCurrentUserInfo]); + }, [RCInstance, currentUser]); const ViewComponent = viewType === 'Popup' ? Popup : Sidebar; From d746198282f319de4c01c3c00d70d97315971350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=99=E4=BB=80=E7=93=A6=E7=89=B9?= <114943221+ShashwatPS@users.noreply.github.com> Date: Thu, 27 Nov 2025 21:03:36 +0530 Subject: [PATCH 110/153] Configured delete while edit (#1021) --- packages/react/src/store/messageStore.js | 3 +++ packages/react/src/views/ChatInput/ChatInput.js | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/react/src/store/messageStore.js b/packages/react/src/store/messageStore.js index c359a0c8eb..4f84f8c1f8 100644 --- a/packages/react/src/store/messageStore.js +++ b/packages/react/src/store/messageStore.js @@ -8,6 +8,7 @@ const useMessageStore = create((set, get) => ({ threadMessages: [], filtered: false, editMessage: {}, + deletedMessage: {}, messagesOffset: 0, quoteMessage: [], deleteMessageRoles: {}, @@ -51,6 +52,7 @@ const useMessageStore = create((set, get) => ({ const message = get().messages.find((m) => m._id === messageId); if (threadMessage) { return set((state) => ({ + deletedMessage: threadMessage, threadMessages: cloneArray(state.threadMessages).filter( (m) => m._id !== messageId ), @@ -58,6 +60,7 @@ const useMessageStore = create((set, get) => ({ } if (message) { return set((state) => ({ + deletedMessage: message, messages: cloneArray(state.messages).filter((m) => m._id !== messageId), })); } diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index ca748520a6..50a342fb92 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -102,6 +102,7 @@ const ChatInput = ({ scrollToBottom }) => { replaceMessage, clearQuoteMessages, threadId, + deletedMessage, } = useMessageStore((state) => ({ editMessage: state.editMessage, setEditMessage: state.setEditMessage, @@ -111,6 +112,7 @@ const ChatInput = ({ scrollToBottom }) => { replaceMessage: state.replaceMessage, threadId: state.threadMainMessage?._id, clearQuoteMessages: state.clearQuoteMessages, + deletedMessage: state.deletedMessage, })); const setIsLoginModalOpen = useLoginStore( @@ -168,6 +170,18 @@ const ChatInput = ({ scrollToBottom }) => { } }, [editMessage]); + useEffect(() => { + if ( + deletedMessage._id && + editMessage._id && + deletedMessage._id === editMessage._id + ) { + messageRef.current.value = ''; + setDisableButton(true); + setEditMessage({}); + } + }, [deletedMessage]); + const getMessageLink = async (id) => { const host = RCInstance.getHost(); const res = await RCInstance.channelInfo(); From 883e3c87544a2abe9e4db7903f26778fdea9dac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=99=E4=BB=80=E7=93=A6=E7=89=B9?= <114943221+ShashwatPS@users.noreply.github.com> Date: Thu, 27 Nov 2025 21:06:11 +0530 Subject: [PATCH 111/153] fixed focus (#1019) --- packages/react/src/views/ChatInput/ChatInput.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 50a342fb92..998e9007cd 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -163,8 +163,10 @@ const ChatInput = ({ scrollToBottom }) => { if (editMessage.attachments) { messageRef.current.value = editMessage.attachments[0]?.description || editMessage.msg; + messageRef.current.focus(); } else if (editMessage.msg) { messageRef.current.value = editMessage.msg; + messageRef.current.focus(); } else { messageRef.current.value = ''; } From 68b0d36896d94feb48c69570b882a864cd50e4ca Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:28:51 +0530 Subject: [PATCH 112/153] FIX: Pin action system message does not render markdown or message content #1022 (#1023) * fixed pinned message not appearing in system message * removed unecessary logs * lint issues * further fixing the lint issues --------- Co-authored-by: Pratheek --- packages/react/src/views/AttachmentHandler/TextAttachment.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/AttachmentHandler/TextAttachment.js b/packages/react/src/views/AttachmentHandler/TextAttachment.js index 423684e4f1..73387c7413 100644 --- a/packages/react/src/views/AttachmentHandler/TextAttachment.js +++ b/packages/react/src/views/AttachmentHandler/TextAttachment.js @@ -2,6 +2,7 @@ import React, { useState, useContext } from 'react'; import { css } from '@emotion/react'; import PropTypes from 'prop-types'; import { Box, Avatar, useTheme, Icon } from '@embeddedchat/ui-elements'; +import { parse } from '@rocket.chat/message-parser'; import AttachmentMetadata from './AttachmentMetadata'; import RCContext from '../../context/RCInstance'; import { Markdown } from '../Markdown'; @@ -114,7 +115,7 @@ const FileAttachment = ({ ) : ( ) From 1b777fb89dbd8c7cc978635eaffd23c0cc206082 Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:01:31 +0530 Subject: [PATCH 113/153] fixing messages not being rendered inside threads (#1033) * fixing messages not being rendered inside threads * removing roomId parameter --- 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 46690e24e6..809b6f4b5c 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -632,7 +632,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const messages = await fetch( - `${this.host}/api/v1/chat.getThreadMessages?roomId=${this.rid}&tmid=${tmid}`, + `${this.host}/api/v1/chat.getThreadMessages?tmid=${tmid}`, { headers: { "Content-Type": "application/json", From 5ada56e2d60d8aea3c58c8eedf45c0183a4d1882 Mon Sep 17 00:00:00 2001 From: srijna Date: Thu, 15 Jan 2026 14:55:49 +0530 Subject: [PATCH 114/153] feat: add unread messages divider with theme-aware styling (#1047) --- packages/react/src/views/ChatBody/ChatBody.js | 30 +++++++++++ .../react/src/views/ChatInput/ChatInput.js | 6 ++- .../react/src/views/ChatLayout/ChatLayout.js | 9 +++- .../react/src/views/Message/Message.styles.js | 52 +++++++++++++++++++ .../react/src/views/Message/MessageDivider.js | 12 +++-- .../src/views/MessageList/MessageList.js | 26 ++++++---- 6 files changed, 120 insertions(+), 15 deletions(-) diff --git a/packages/react/src/views/ChatBody/ChatBody.js b/packages/react/src/views/ChatBody/ChatBody.js index eeae34dfb7..34f5c8bf40 100644 --- a/packages/react/src/views/ChatBody/ChatBody.js +++ b/packages/react/src/views/ChatBody/ChatBody.js @@ -40,6 +40,7 @@ const ChatBody = ({ showRoles, messageListRef, scrollToBottom, + clearUnreadDividerRef, }) => { const { classNames, styleOverrides } = useComponentOverrides('ChatBody'); const { theme, mode } = useTheme(); @@ -49,6 +50,8 @@ const ChatBody = ({ const [, setIsUserScrolledUp] = useState(false); const [otherUserMessage, setOtherUserMessage] = useState(false); const [isOverflowing, setIsOverflowing] = useState(false); + const [firstUnreadMessageId, setFirstUnreadMessageId] = useState(null); + const pendingFirstUnreadRef = useRef(null); const { RCInstance, ECOptions } = useContext(RCContext); const showAnnouncement = ECOptions?.showAnnouncement; const messages = useMessageStore((state) => state.messages); @@ -124,6 +127,10 @@ const ChatBody = ({ const isScrolledUp = messageListRef?.current?.scrollTop !== 0; if (isScrolledUp && !('pinned' in message) && !('starred' in message)) { setOtherUserMessage(true); + // Track the first unread message (only set if not already tracking) + if (!pendingFirstUnreadRef.current) { + pendingFirstUnreadRef.current = message._id; + } } } upsertMessage(message, ECOptions?.enableThreads); @@ -177,7 +184,22 @@ const ChatBody = ({ }); }, []); + // Expose clearUnreadDivider function via ref for ChatInput to call + useEffect(() => { + if (clearUnreadDividerRef) { + clearUnreadDividerRef.current = () => { + setFirstUnreadMessageId(null); + pendingFirstUnreadRef.current = null; + }; + } + }, [clearUnreadDividerRef]); + const handlePopupClick = () => { + // Set the unread divider to show above the first unread message + if (pendingFirstUnreadRef.current) { + setFirstUnreadMessageId(pendingFirstUnreadRef.current); + pendingFirstUnreadRef.current = null; + } scrollToBottom(); setIsUserScrolledUp(false); setOtherUserMessage(false); @@ -242,6 +264,12 @@ const ChatBody = ({ setPopupVisible(false); setIsUserScrolledUp(false); setOtherUserMessage(false); + // Clear unread divider when scrolled to bottom + if (firstUnreadMessageId) { + setFirstUnreadMessageId(null); + } + // Also clear pending unread ref + pendingFirstUnreadRef.current = null; } }, [ messageListRef, @@ -258,6 +286,7 @@ const ChatBody = ({ setIsUserScrolledUp, setPopupVisible, setOtherUserMessage, + firstUnreadMessageId, ]); const showNewMessagesPopup = () => { @@ -392,6 +421,7 @@ const ChatBody = ({ loadingOlderMessages={loadingOlderMessages} isUserAuthenticated={isUserAuthenticated} hasMoreMessages={hasMoreMessages} + firstUnreadMessageId={firstUnreadMessageId} /> )} diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 998e9007cd..c30e9c03cf 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -35,7 +35,7 @@ import useSearchMentionUser from '../../hooks/useSearchMentionUser'; import formatSelection from '../../lib/formatSelection'; import { parseEmoji } from '../../lib/emoji'; -const ChatInput = ({ scrollToBottom }) => { +const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const { styleOverrides, classNames } = useComponentOverrides('ChatInput'); const { RCInstance, ECOptions } = useRCContext(); const { theme } = useTheme(); @@ -398,6 +398,10 @@ const ChatInput = ({ scrollToBottom }) => { handleSendNewMessage(message); scrollToBottom(); + // Clear unread divider when user sends a message + if (clearUnreadDividerRef?.current) { + clearUnreadDividerRef.current(); + } }; const sendAttachment = (event) => { diff --git a/packages/react/src/views/ChatLayout/ChatLayout.js b/packages/react/src/views/ChatLayout/ChatLayout.js index 60c935a360..f3b4262acb 100644 --- a/packages/react/src/views/ChatLayout/ChatLayout.js +++ b/packages/react/src/views/ChatLayout/ChatLayout.js @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useCallback, useState } from 'react'; +import React, { useEffect, useRef, useCallback } from 'react'; import { Box, useComponentOverrides } from '@embeddedchat/ui-elements'; import styles from './ChatLayout.styles'; import { @@ -35,6 +35,7 @@ import useUiKitStore from '../../store/uiKitStore'; const ChatLayout = () => { const messageListRef = useRef(null); + const clearUnreadDividerRef = useRef(null); const { classNames, styleOverrides } = useComponentOverrides('ChatBody'); const { RCInstance, ECOptions } = useRCContext(); const anonymousMode = ECOptions?.anonymousMode; @@ -113,8 +114,12 @@ const ChatLayout = () => { showRoles={showRoles} messageListRef={messageListRef} scrollToBottom={scrollToBottom} + clearUnreadDividerRef={clearUnreadDividerRef} + /> + -
diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index 75253043e2..9099f72ec2 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -108,6 +108,58 @@ export const getMessageDividerStyles = (theme) => { return styles; }; +export const getUnreadMessageDividerStyles = (theme, mode) => { + // Use destructive (red) for light themes, warningForeground (orange) for dark themes + const dividerColor = + mode === 'light' + ? theme.colors.destructive + : theme.colors.warningForeground; + + const styles = { + divider: css` + letter-spacing: 0rem; + font-size: 0.75rem; + font-weight: 700; + line-height: 1rem; + position: relative; + display: flex; + z-index: 1000; + align-items: center; + margin-top: 0.5rem; + margin-bottom: 0.75rem; + padding-left: 1.25rem; + padding-right: 1.25rem; + @media (max-width: 780px) { + z-index: 1; + } + `, + + dividerContent: css` + margin-top: 0.5rem; + margin-bottom: 0.5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + background-color: ${theme.colors.background}; + color: ${dividerColor}; + position: absolute; + left: 50%; + transform: translateX(-50%); + border-radius: ${theme.radius}; + `, + + bar: css` + display: flex; + justify-content: flex-end; + align-items: center; + flex-grow: 1; + height: 1px; + background-color: ${dividerColor}; + `, + }; + + return styles; +}; + export const getMessageHeaderStyles = (theme) => { const styles = { header: css` diff --git a/packages/react/src/views/Message/MessageDivider.js b/packages/react/src/views/Message/MessageDivider.js index 617b7615cf..9c8f1c26bf 100644 --- a/packages/react/src/views/Message/MessageDivider.js +++ b/packages/react/src/views/Message/MessageDivider.js @@ -6,11 +6,15 @@ import { useTheme, } from '@embeddedchat/ui-elements'; -import { getMessageDividerStyles } from './Message.styles'; +import { + getMessageDividerStyles, + getUnreadMessageDividerStyles, +} from './Message.styles'; export const MessageDivider = ({ children, unreadLabel, + unread = false, className = '', style = {}, ...props @@ -20,8 +24,10 @@ export const MessageDivider = ({ className, style ); - const { theme } = useTheme(); - const styles = getMessageDividerStyles(theme); + const { theme, mode } = useTheme(); + const styles = unread + ? getUnreadMessageDividerStyles(theme, mode) + : getMessageDividerStyles(theme); return ( { const showReportMessage = useMessageStore((state) => state.showReportMessage); const messageToReport = useMessageStore((state) => state.messageToReport); @@ -86,17 +88,23 @@ const MessageList = ({ const sequential = isMessageSequential(msg, prev, 300); const lastSequential = sequential && isMessageLastSequential(msg, next); + const showUnreadDivider = + firstUnreadMessageId && msg._id === firstUnreadMessageId; return ( - + + {showUnreadDivider && ( + Unread Messages + )} + + ); })} {showReportMessage && ( From 7a241ab28931463c7b5f23ebe56705bc33229051 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:01:46 +0530 Subject: [PATCH 115/153] feat: Enhance video recorder (#831) * Enhance video recorder * Updated Config * Fixed Bug - Disabled Send button should not work after the click --------- Co-authored-by: Zishan Ahmad --- packages/react/src/hooks/useMediaRecorder.js | 79 +++++- .../src/views/ChatInput/ChatInput.styles.js | 31 ++- .../views/ChatInput/VideoMessageRecoder.js | 254 +++++++++++------- .../src/components/Icon/icons/Record.js | 25 ++ .../src/components/Icon/icons/StopRecord.js | 25 ++ .../src/components/Icon/icons/index.js | 4 + 6 files changed, 322 insertions(+), 96 deletions(-) create mode 100644 packages/ui-elements/src/components/Icon/icons/Record.js create mode 100644 packages/ui-elements/src/components/Icon/icons/StopRecord.js diff --git a/packages/react/src/hooks/useMediaRecorder.js b/packages/react/src/hooks/useMediaRecorder.js index 05046a59de..028b0d9b1d 100644 --- a/packages/react/src/hooks/useMediaRecorder.js +++ b/packages/react/src/hooks/useMediaRecorder.js @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; function useUserMedia(constraints, videoRef) { const [stream, setStream] = useState(); @@ -47,3 +47,80 @@ export function useMediaRecorder({ constraints, onStop, videoRef }) { return [start, stop]; } + +export function useNewMediaRecorder({ constraints, videoRef, onStop }) { + const [stream, setStream] = useState(); + const [isStreaming, setIsStreaming] = useState(false); + const [recorder, setRecorder] = useState(); + const [recordingData, setRecordingData] = useState(null); + const chunks = useRef([]); + + async function startCameraAndMic() { + if (isStreaming) return; + try { + const _stream = await navigator.mediaDevices.getUserMedia(constraints); + setStream(_stream); + setIsStreaming(true); + if (videoRef.current) { + videoRef.current.srcObject = _stream; + } + } catch (error) { + console.error('Error starting camera and mic:', error); + } + } + + async function startRecording() { + if (!isStreaming) { + console.error('Camera and mic must be on to start recording.'); + return; + } + + chunks.current = []; + const _recorder = new MediaRecorder(stream); + _recorder.start(); + setRecorder(_recorder); + + _recorder.addEventListener('dataavailable', (event) => { + chunks.current.push(event.data); + }); + + _recorder.addEventListener('stop', () => { + setRecordingData(new Blob(chunks.current, { type: 'video/mp4' })); + onStop && onStop(chunks.current); + }); + } + + async function stopRecording() { + if (recorder && recorder.state === 'recording') { + recorder.stop(); + } + } + + function getRecording() { + return recordingData; + } + + function deleteRecording() { + setRecordingData(null); + chunks.current = []; + } + + function stopCameraAndMic() { + if (stream) { + stream.getTracks().forEach((track) => track.stop()); + setStream(null); + setIsStreaming(false); + } + } + + useEffect(() => () => stopCameraAndMic(), []); + + return { + startCameraAndMic, + startRecording, + stopRecording, + getRecording, + deleteRecording, + stopCameraAndMic, + }; +} diff --git a/packages/react/src/views/ChatInput/ChatInput.styles.js b/packages/react/src/views/ChatInput/ChatInput.styles.js index b193d06922..0841451324 100644 --- a/packages/react/src/views/ChatInput/ChatInput.styles.js +++ b/packages/react/src/views/ChatInput/ChatInput.styles.js @@ -118,21 +118,48 @@ export const getCommonRecorderStyles = (theme) => { border-radius: 50%; background-color: ${theme.colors.destructive}; margin: auto; - margin-right: 8px; + margin-right: 5px; + margin-left: 5px; + `, + + oppositeDot: css` + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background-color: ${theme.colors.background}; + margin: auto; + margin-right: 5px; + margin-left: 5px; `, controller: css` - gap: 0.15rem; + width: 100%; display: inline-flex; `, timer: css` margin: auto; `, + + spacer: css` + flex-grow: 1; + `, + record: css` display: flex; margin: auto; `, + + leftSection: css` + display: flex; + align-items: left; + `, + + rightSection: css` + display: flex; + align-items: right; + margin-top: 0.3rem; + `, modal: { '@media(max-width: 768px)': { height: '100%', diff --git a/packages/react/src/views/ChatInput/VideoMessageRecoder.js b/packages/react/src/views/ChatInput/VideoMessageRecoder.js index 814c2093aa..f153d4c697 100644 --- a/packages/react/src/views/ChatInput/VideoMessageRecoder.js +++ b/packages/react/src/views/ChatInput/VideoMessageRecoder.js @@ -7,94 +7,52 @@ import { Tooltip, Modal, useTheme, + Button, + lighten, + darken, } from '@embeddedchat/ui-elements'; -import { useMediaRecorder } from '../../hooks/useMediaRecorder'; +import { useNewMediaRecorder } from '../../hooks/useMediaRecorder'; import useMessageStore from '../../store/messageStore'; import { getCommonRecorderStyles } from './ChatInput.styles'; import useAttachmentWindowStore from '../../store/attachmentwindow'; const VideoMessageRecorder = (props) => { const videoRef = useRef(null); + const [isRecording, setIsRecording] = useState(false); const { disabled, displayName, popOverItemStyles } = props; const { theme } = useTheme(); + const { mode } = useTheme(); const styles = getCommonRecorderStyles(theme); - const toogleRecordingMessage = useMessageStore( - (state) => state.toogleRecordingMessage - ); - - const { toggle, setData } = useAttachmentWindowStore((state) => ({ - toggle: state.toggle, - setData: state.setData, - })); + const [state, setRecordState] = useState('idle'); // 1. idle, 2. preview. - const [state, setRecordState] = useState('idle'); const [time, setTime] = useState('00:00'); const [recordingInterval, setRecordingInterval] = useState(null); const [file, setFile] = useState(null); - const [isRecorded, setIsRecorded] = useState(false); - const onStop = (videoChunks) => { - const videoBlob = new Blob(videoChunks, { type: 'video/mp4' }); - const fileName = 'Video record.mp4'; - setFile(new File([videoBlob], fileName, { type: 'video/mp4' })); - }; + const [isSendDisabled, setIsSendDisabled] = useState(true); - const [start, stop] = useMediaRecorder({ - constraints: { audio: true, video: true }, // Update constraints as needed - onStop, + const { toggle, setData } = useAttachmentWindowStore((state_) => ({ + toggle: state_.toggle, + setData: state_.setData, + })); + + const { + startCameraAndMic, + startRecording, + stopRecording, + deleteRecording, + stopCameraAndMic, + } = useNewMediaRecorder({ + constraints: { video: true, audio: true }, videoRef, + onStop: (videoChunks) => { + const videoBlob = new Blob(videoChunks, { type: 'video/mp4' }); + const fileName = 'Video record.mp4'; + setFile(new File([videoBlob], fileName, { type: 'video/mp4' })); + }, }); - const stopRecording = async () => { - stop(); - if (recordingInterval) { - clearInterval(recordingInterval); - } - setRecordingInterval(null); - setTime('00:00'); - setRecordState('idle'); - }; - - const handleRecordButtonClick = () => { - if (disabled) return; - setRecordState('recording'); - try { - start(videoRef.current); - toogleRecordingMessage(); - const startTime = new Date(); - setRecordingInterval( - setInterval(() => { - const now = new Date(); - const distance = (now.getTime() - startTime.getTime()) / 1000; - const minutes = Math.floor(distance / 60); - const seconds = Math.floor(distance % 60); - setTime( - `${String(minutes).padStart(2, '0')}:${String(seconds).padStart( - 2, - '0' - )}` - ); - }, 1000) - ); - } catch (error) { - console.log(error); - setRecordState('idle'); - } - }; - - const handleCancelRecordButton = async () => { - toogleRecordingMessage(); - await stopRecording(); - setIsRecorded(false); - }; - - const handleStopRecordButton = async () => { - toogleRecordingMessage(); - setIsRecorded(true); - await stopRecording(); - }; - const handleMount = useCallback(async () => { if (navigator.permissions) { try { @@ -134,16 +92,77 @@ const VideoMessageRecorder = (props) => { handleMount(); }, [handleMount]); - useEffect(() => { - if (isRecorded && file) { - toggle(); - setData(file); - setIsRecorded(false); + const startRecordingInterval = () => { + const startTime = new Date(); + setRecordingInterval( + setInterval(() => { + const now = new Date(); + const distance = (now.getTime() - startTime.getTime()) / 1000; + const minutes = Math.floor(distance / 60); + const seconds = Math.floor(distance % 60); + setTime( + `${String(minutes).padStart(2, '0')}:${String(seconds).padStart( + 2, + '0' + )}` + ); + }, 1000) + ); + }; + + const stopRecordingInterval = () => { + if (recordingInterval) { + clearInterval(recordingInterval); } + setRecordingInterval(null); + }; + + const deleteRecordingInterval = () => { + stopRecordingInterval(); + setTime('00:00'); + }; + + const openWindowToRecord = () => { + startCameraAndMic(); + setRecordState('preview'); + }; + + const handleStartRecording = () => { + deleteRecordingInterval(); + setIsRecording(true); + startRecording(); + startRecordingInterval(); + setIsSendDisabled(true); + }; + + const handleStopRecording = () => { + stopRecording(); + stopRecordingInterval(); + setIsRecording(false); + setIsSendDisabled(false); + }; + + const handleSendRecording = () => { + if (isRecording) return; if (file) { - setFile(null); + toggle(); + setData(file); } - }, [isRecorded, file]); + deleteRecordingInterval(); + deleteRecording(); + stopCameraAndMic(); + setRecordState('idle'); + setIsSendDisabled(true); + }; + + const closeWindowStopRecord = () => { + stopRecording(); + deleteRecordingInterval(); + deleteRecording(); + stopCameraAndMic(); + setRecordState('idle'); + setIsSendDisabled(true); + }; return ( <> @@ -152,7 +171,7 @@ const VideoMessageRecorder = (props) => { @@ -164,21 +183,21 @@ const VideoMessageRecorder = (props) => { ghost square disabled={disabled} - onClick={handleRecordButtonClick} + onClick={openWindowToRecord} > ))} - {state === 'recording' && ( + {state === 'preview' && ( <> diff --git a/packages/ui-elements/src/components/Icon/icons/Record.js b/packages/ui-elements/src/components/Icon/icons/Record.js new file mode 100644 index 0000000000..aa5480cc23 --- /dev/null +++ b/packages/ui-elements/src/components/Icon/icons/Record.js @@ -0,0 +1,25 @@ +import React from 'react'; + +const Record = (props) => ( + + + + +); + +export default Record; diff --git a/packages/ui-elements/src/components/Icon/icons/StopRecord.js b/packages/ui-elements/src/components/Icon/icons/StopRecord.js new file mode 100644 index 0000000000..c4adda4cd2 --- /dev/null +++ b/packages/ui-elements/src/components/Icon/icons/StopRecord.js @@ -0,0 +1,25 @@ +import React from 'react'; + +const StopRecord = (props) => ( + + + + +); + +export default StopRecord; diff --git a/packages/ui-elements/src/components/Icon/icons/index.js b/packages/ui-elements/src/components/Icon/icons/index.js index 234251edab..1f416a020a 100644 --- a/packages/ui-elements/src/components/Icon/icons/index.js +++ b/packages/ui-elements/src/components/Icon/icons/index.js @@ -37,6 +37,8 @@ import Kebab from './Kebab'; import Check from './Check'; import ErrorCircle from './ErrorCircle'; import ArrowDown from './ArrowDown'; +import Record from './Record'; +import StopRecord from './StopRecord'; import PinFilled from './PinFilled'; import VideoRecorder from './VideoRecoder'; import DisabledRecorder from './DisableRecorder'; @@ -109,6 +111,8 @@ const icons = { check: Check, 'error-circle': ErrorCircle, 'arrow-down': ArrowDown, + record: Record, + 'stop-record': StopRecord, 'pin-filled': PinFilled, clipboard: Clipboard, clip: Clip, From adfd0eeb035aa9575a8e51a8f0dc66f759a251b1 Mon Sep 17 00:00:00 2001 From: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:03:24 +0530 Subject: [PATCH 116/153] Feat: Added 'Online User Status' Filter to Members List (#782) * Feat: Add 'Online User Status' Filter to Members List * Added space between InviteButton and search bar-user status filter * fixed lint import error * Added Divider along with Display count and total count of members * Updated count logic with RC * Removed API Call to fetch status of user --------- Co-authored-by: Zishan Ahmad --- .../react/src/views/RoomMembers/RoomMember.js | 96 +++++++++++++++---- .../src/views/RoomMembers/RoomMemberItem.js | 21 +--- .../views/RoomMembers/RoomMembers.styles.js | 8 +- 3 files changed, 87 insertions(+), 38 deletions(-) diff --git a/packages/react/src/views/RoomMembers/RoomMember.js b/packages/react/src/views/RoomMembers/RoomMember.js index f60c3476f9..27a97278fc 100644 --- a/packages/react/src/views/RoomMembers/RoomMember.js +++ b/packages/react/src/views/RoomMembers/RoomMember.js @@ -7,9 +7,12 @@ import { Sidebar, Input, Popup, + StaticSelect, + Divider, useComponentOverrides, useTheme, } from '@embeddedchat/ui-elements'; +import { css } from '@emotion/react'; import RoomMemberItem from './RoomMemberItem'; import RCContext, { useRCContext } from '../../context/RCInstance'; import useInviteStore from '../../store/inviteStore'; @@ -37,6 +40,8 @@ const RoomMembers = ({ members }) => { const [searchTerm, setSearchTerm] = useState(''); const [filteredMembers, setFilteredMembers] = useState(members); + const [viewStatus, setViewStatus] = useState('All'); + useEffect(() => { const getUserInfo = async () => { try { @@ -52,19 +57,32 @@ const RoomMembers = ({ members }) => { }, [RCInstance]); useEffect(() => { + const filtered = members.filter((member) => { + if (viewStatus === 'Online') { + return member.status === 'online'; + } + return true; + }); + setFilteredMembers( - members.filter( + filtered.filter( (member) => member.name?.toLowerCase().includes(searchTerm.toLowerCase()) || member.username?.toLowerCase().includes(searchTerm.toLowerCase()) ) ); - }, [searchTerm, members]); + }, [viewStatus, searchTerm, members]); const roles = userInfo && userInfo.roles ? userInfo.roles : []; const isAdmin = roles.includes('admin'); const ViewComponent = viewType === 'Popup' ? Popup : Sidebar; + const handleSelect = (value) => { + setViewStatus(value); + }; + + const displayedMembers = filteredMembers.length; + return ( { <> {isAdmin && ( )} - - setSearchTerm(e.target.value)} - placeholder="Search members" - /> - + + + setSearchTerm(e.target.value)} + placeholder="Search members" + /> + + + + + + + + + + + + + Showing {displayedMembers} of {displayedMembers} {filteredMembers.length > 0 ? ( filteredMembers.map((member) => ( - <> - - + )) ) : ( No members found diff --git a/packages/react/src/views/RoomMembers/RoomMemberItem.js b/packages/react/src/views/RoomMembers/RoomMemberItem.js index f5ff504035..d53632782d 100644 --- a/packages/react/src/views/RoomMembers/RoomMemberItem.js +++ b/packages/react/src/views/RoomMembers/RoomMemberItem.js @@ -1,34 +1,17 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { css } from '@emotion/react'; import { Box, Icon, Avatar, useTheme } from '@embeddedchat/ui-elements'; -import RCContext from '../../context/RCInstance'; import { RoomMemberItemStyles } from './RoomMembers.styles'; import useSetExclusiveState from '../../hooks/useSetExclusiveState'; import { useUserStore } from '../../store'; -const RoomMemberItem = ({ user, host }) => { - const { RCInstance } = useContext(RCContext); - const [userStatus, setUserStatus] = useState(''); +const RoomMemberItem = ({ user, host, userStatus }) => { const avatarUrl = new URL(`avatar/${user.username}`, host).toString(); const { theme } = useTheme(); const { mode } = useTheme(); const styles = RoomMemberItemStyles(theme, mode); - useEffect(() => { - const getStatus = async () => { - try { - const res = await RCInstance.getUserStatus(user._id); - if (res.success) { - setUserStatus(res.status); - } - } catch (err) { - console.error('Error fetching user status:', err); - } - }; - - getStatus(); - }, [RCInstance]); const setExclusiveState = useSetExclusiveState(); const { setShowCurrentUserInfo, setCurrentUser } = useUserStore((state) => ({ setShowCurrentUserInfo: state.setShowCurrentUserInfo, diff --git a/packages/react/src/views/RoomMembers/RoomMembers.styles.js b/packages/react/src/views/RoomMembers/RoomMembers.styles.js index 415940c2f4..1ff0181b7f 100644 --- a/packages/react/src/views/RoomMembers/RoomMembers.styles.js +++ b/packages/react/src/views/RoomMembers/RoomMembers.styles.js @@ -19,7 +19,13 @@ export const getRoomMemberStyles = (theme) => { padding: 0 0.5rem; border-radius: ${theme.radius}; position: relative; - margin-top: 1rem; + width: 60%; + `, + filterContainer: css` + width: 40%; + margin-left: 0.5rem; + border-radius: ${theme.radius}; + position: relative; `, textInput: css` flex: 1; From 2de96a8e058ffbe47ec58e8c05d942572c66595b Mon Sep 17 00:00:00 2001 From: Kartik Date: Thu, 15 Jan 2026 15:30:18 +0530 Subject: [PATCH 117/153] fix: improve system message layout and avatar consistency (#1061) * fix: improve system message layout and avatar consistency - Add flex-wrap to message header for proper text wrapping - Move system action text to separate line using CSS order - Make avatar sizes consistent across all message types - Remove conditional avatar sizing for system messages * Added the avatar size back to original size and keep the username truncation fix * fix: prevent username truncation in system messages and add tooltip for long action text --------- Co-authored-by: dodaa08 --- packages/react/src/views/Message/Message.styles.js | 8 ++------ packages/react/src/views/Message/MessageHeader.js | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index 9099f72ec2..5cdf333662 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -179,10 +179,8 @@ export const getMessageHeaderStyles = (theme) => { font-size: 0.875rem; font-weight: 700; line-height: 1.25rem; - overflow: hidden; - text-overflow: ellipsis; white-space: nowrap; - flex-shrink: 1; + flex-shrink: 0; `, userName: css` @@ -191,10 +189,8 @@ export const getMessageHeaderStyles = (theme) => { letter-spacing: 0rem; font-size: 0.875rem; line-height: 1.25rem; - overflow: hidden; - text-overflow: ellipsis; white-space: nowrap; - flex-shrink: 1; + flex-shrink: 0; `, userRole: css` diff --git a/packages/react/src/views/Message/MessageHeader.js b/packages/react/src/views/Message/MessageHeader.js index a93a46e243..a4bff59b10 100644 --- a/packages/react/src/views/Message/MessageHeader.js +++ b/packages/react/src/views/Message/MessageHeader.js @@ -165,6 +165,7 @@ const MessageHeader = ({ css={styles.userActions} className={appendClassNames('ec-message-header-useractions')} style={{ marginLeft: '2px' }} + title={userActions()} > {userActions()} From b5f374c8e3fec491d4d3f801c0d23d640973edce Mon Sep 17 00:00:00 2001 From: Kartik Date: Thu, 15 Jan 2026 15:47:08 +0530 Subject: [PATCH 118/153] feat: detect links in user bio (#1056) * fix: add URL detection and linking in user bio fields - Add parseUrls utility function to detect URLs in bio text - Convert detected URLs to clickable links with proper styling - Match email link styling for consistency - Support both http/https URLs and URLs without protocol * fix: add React import back to UserInformation component * refactor: use @rocket.chat/message-parser instead of custom URL regex --------- Co-authored-by: dodaa08 --- .../react/src/views/UserInformation/UserInformation.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/react/src/views/UserInformation/UserInformation.js b/packages/react/src/views/UserInformation/UserInformation.js index de79395c50..a544bd334b 100644 --- a/packages/react/src/views/UserInformation/UserInformation.js +++ b/packages/react/src/views/UserInformation/UserInformation.js @@ -1,5 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { css } from '@emotion/react'; +import { parse } from '@rocket.chat/message-parser'; import { Box, Sidebar, @@ -18,6 +19,7 @@ import formatTimestampGetDate from '../../lib/formatTimestampGetDate'; import UserInfoField from './UserInfoField'; import getUserInformationStyles from './UserInformation.styles'; import useSetExclusiveState from '../../hooks/useSetExclusiveState'; +import { Markdown } from '../Markdown'; const UserInformation = () => { const { variantOverrides } = useComponentOverrides('UserInformation'); @@ -176,7 +178,12 @@ const UserInformation = () => { {currentUserInfo?.bio && ( + } isAdmin={isAllowedToViewFullInfo} authenticatedUserId={authenticatedUserId} currentUserInfo={currentUserInfo} From 4c3cfd1a5dabe22e3dbc6131b9dfe311131554b7 Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:50:51 +0530 Subject: [PATCH 119/153] Fixed slash commands entered in threads displayed in main chat (#1042) * Fixed slash commands entered in threads displayed in main chat * Fixing lint issues --- packages/api/src/EmbeddedChatApi.ts | 11 ++++++++++- packages/react/src/views/ChatInput/ChatInput.js | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 809b6f4b5c..f55f55d58f 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -1191,7 +1191,15 @@ export default class EmbeddedChatApi { return data; } - async execCommand({ command, params }: { command: string; params: string }) { + async execCommand({ + command, + params, + tmid, + }: { + command: string; + params: string; + tmid?: string; + }) { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/commands.run`, { headers: { @@ -1203,6 +1211,7 @@ export default class EmbeddedChatApi { body: JSON.stringify({ command, params, + tmid, roomId: this.rid, triggerId: Math.random().toString(32).slice(2, 20), }), diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index c30e9c03cf..e753b689ae 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -354,7 +354,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const handleCommandExecution = async (message) => { const execCommand = async (command, params) => { - await RCInstance.execCommand({ command, params }); + await RCInstance.execCommand({ command, params, tmid: threadId }); setFilteredCommands([]); }; From 1fb4274ee6504fe50168ab467c5ba414f46907b6 Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:53:45 +0530 Subject: [PATCH 120/153] fix: replace import assertion with readFileSync in rollup configs (#1052) * fix: replace import assertion with readFileSync in rollup configs - Replace 'import packageJson from ./package.json assert { type: 'json' }' with readFileSync - Fixes build error: SyntaxError: Unexpected identifier 'assert' - Affects packages/auth and packages/api rollup.config.js files - Uses Node.js fs.readFileSync which is compatible with Rollup parser * fix: use 'with' syntax for JSON import assertions in rollup configs - Replace readFileSync with 'with { type: 'json' }' import syntax - Uses newer standard import attributes syntax * fix: use createRequire with explicit path resolution for Node 16 compatibility - Replace 'with { type: 'json' }' import syntax with createRequire approach - Add fileURLToPath and path.dirname for explicit path resolution - Ensures backward compatibility with Node.js 16.19.0 - Fixes build error during yarn install postinstall script - Affects packages/auth and packages/api rollup.config.js files --- packages/api/rollup.config.js | 8 +++++++- packages/auth/rollup.config.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/api/rollup.config.js b/packages/api/rollup.config.js index 5351b95f5f..6ce2f32df8 100644 --- a/packages/api/rollup.config.js +++ b/packages/api/rollup.config.js @@ -1,7 +1,13 @@ import dts from 'rollup-plugin-dts' import esbuild from 'rollup-plugin-esbuild' -import packageJson from './package.json' assert { type: 'json' }; import path from 'path'; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const require = createRequire(import.meta.url); +const packageJson = require(path.resolve(__dirname, './package.json')); const name = packageJson.main.replace(/\.js$/, ''); diff --git a/packages/auth/rollup.config.js b/packages/auth/rollup.config.js index ab1343496a..6ce2f32df8 100644 --- a/packages/auth/rollup.config.js +++ b/packages/auth/rollup.config.js @@ -1,7 +1,13 @@ import dts from 'rollup-plugin-dts' import esbuild from 'rollup-plugin-esbuild' import path from 'path'; -import packageJson from './package.json' assert { type: 'json' }; +import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const require = createRequire(import.meta.url); +const packageJson = require(path.resolve(__dirname, './package.json')); const name = packageJson.main.replace(/\.js$/, ''); From c706b678180ee855e2e1460aa3caa05504cfcaca Mon Sep 17 00:00:00 2001 From: Kartik Date: Thu, 15 Jan 2026 22:50:56 +0530 Subject: [PATCH 121/153] fix: mobile header layout overlap (#1066) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: prevent header icon row from overlapping channel name on mobile * fix: prevent header icon row from overlapping channel name on mobile - Add flex-wrap to header for natural content wrapping - Set min-width on channel description to prevent over-shrinking - Add responsive styles for icon row at narrow widths (≤380px) --------- Co-authored-by: dodaa08 --- .../react/src/views/ChatHeader/ChatHeader.js | 3 +++ .../src/views/ChatHeader/ChatHeader.styles.js | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 9143598d30..0986104ae5 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -399,6 +399,9 @@ const ChatHeader = ({
{channelInfo.name || channelName || 'channelName'} diff --git a/packages/react/src/views/ChatHeader/ChatHeader.styles.js b/packages/react/src/views/ChatHeader/ChatHeader.styles.js index 46a9ecd32c..5ae8b52647 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.styles.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.styles.js @@ -18,6 +18,8 @@ const getChatHeaderStyles = ({ theme, mode }) => { padding: 0 0.75rem; justify-content: space-between; width: 100%; + flex-wrap: wrap; + gap: 0.5rem; `, chatHeaderParent: css` @@ -42,20 +44,30 @@ const getChatHeaderStyles = ({ theme, mode }) => { channelDescription: css` ${rowCentreAlign} flex: 1; - min-width: 0; + min-width: 120px; gap: 0.5rem; + overflow: hidden; `, chatHeaderIconRow: css` ${rowCentreAlign} - position:relative; + position: relative; gap: 0.5rem; + flex-shrink: 0; + @media (max-width: 380px) { + gap: 0.25rem; + flex-wrap: wrap; + justify-content: flex-end; + } `, channelName: css` display: flex; align-items: center; gap: 0.1rem; cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `, channelTopic: css` opacity: 0.8rem; From 89e85b77224176e1ecb991b4cae8c14b9f56f28f Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Thu, 22 Jan 2026 01:34:31 +0530 Subject: [PATCH 122/153] fix(api): escape json body in api calls to prevent injection (#1072) * fix(api): escape json body in api calls to prevent injection * fix(api): replace manual JSON string construction with JSON.stringify to prevent injection - Replaced template literal JSON construction with JSON.stringify() in all API methods - Fixes vulnerability where user input containing quotes would break JSON structure - Affected methods: updateMessage, deleteMessage, updateUserUsername, reactToMessage, reportMessage, starMessage, unstarMessage, pinMessage, unpinMessage, updateUserNameThroughSuggestion --- packages/api/src/EmbeddedChatApi.ts | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index f55f55d58f..72e25a0466 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -414,7 +414,10 @@ export default class EmbeddedChatApi { if (suggestedUsername.success) { const response2 = await fetch(`${this.host}/api/v1/users.update`, { - body: `{"userId": "${userid}", "data": { "username": "${suggestedUsername.result}" }}`, + body: JSON.stringify({ + userId: userid, + data: { username: suggestedUsername.result }, + }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -439,7 +442,10 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/users.update`, { - body: `{"userId": "${userid}", "data": { "username": "${newUserName}" }}`, + body: JSON.stringify({ + userId: userid, + data: { username: newUserName }, + }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -776,7 +782,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.delete`, { - body: `{"roomId": "${this.rid}", "msgId": "${msgId}"}`, + body: JSON.stringify({ roomId: this.rid, msgId }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -794,7 +800,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.update`, { - body: `{"roomId": "${this.rid}", "msgId": "${msgId}","text" : "${text}" }`, + body: JSON.stringify({ roomId: this.rid, msgId, text }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -854,7 +860,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.starMessage`, { - body: `{"messageId": "${mid}"}`, + body: JSON.stringify({ messageId: mid }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -872,7 +878,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.unStarMessage`, { - body: `{"messageId": "${mid}"}`, + body: JSON.stringify({ messageId: mid }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -950,7 +956,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.pinMessage`, { - body: `{"messageId": "${mid}"}`, + body: JSON.stringify({ messageId: mid }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -970,7 +976,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.unPinMessage`, { - body: `{"messageId": "${mid}"}`, + body: JSON.stringify({ messageId: mid }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -988,7 +994,11 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.react`, { - body: `{"messageId": "${messageId}", "emoji": "${emoji}", "shouldReact": ${shouldReact}}`, + body: JSON.stringify({ + messageId, + emoji, + shouldReact, + }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, @@ -1006,7 +1016,7 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch(`${this.host}/api/v1/chat.reportMessage`, { - body: `{"messageId": "${messageId}", "description": "${description}"}`, + body: JSON.stringify({ messageId, description }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, From 52d26b51cec59d11d1cca27fb9afb97082ef7f9d Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Mon, 9 Feb 2026 10:32:44 +0530 Subject: [PATCH 123/153] fix: infinite render loop in media recorders by adding missing dependency arrays to useCallback (#1090) --- packages/react/src/views/ChatInput/AudioMessageRecorder.js | 2 +- packages/react/src/views/ChatInput/VideoMessageRecoder.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/views/ChatInput/AudioMessageRecorder.js b/packages/react/src/views/ChatInput/AudioMessageRecorder.js index 53dbddf4bd..35e8063a52 100644 --- a/packages/react/src/views/ChatInput/AudioMessageRecorder.js +++ b/packages/react/src/views/ChatInput/AudioMessageRecorder.js @@ -119,7 +119,7 @@ const AudioMessageRecorder = (props) => { } catch (error) { console.warn(error); } - }); + }, []); useEffect(() => { handleMount(); diff --git a/packages/react/src/views/ChatInput/VideoMessageRecoder.js b/packages/react/src/views/ChatInput/VideoMessageRecoder.js index f153d4c697..b45c821757 100644 --- a/packages/react/src/views/ChatInput/VideoMessageRecoder.js +++ b/packages/react/src/views/ChatInput/VideoMessageRecoder.js @@ -86,7 +86,7 @@ const VideoMessageRecorder = (props) => { } catch (error) { console.warn(error); } - }); + }, []); useEffect(() => { handleMount(); From daf291df7d9e159b67b8b83df501879cee1d5d7b Mon Sep 17 00:00:00 2001 From: "khizar (RinX)" <109973520+Khizarshah01@users.noreply.github.com> Date: Fri, 13 Mar 2026 09:45:22 +0530 Subject: [PATCH 124/153] feat: add clickable timestamp to quoted messages (#1089) --- .../views/AttachmentHandler/TextAttachment.js | 84 +++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/packages/react/src/views/AttachmentHandler/TextAttachment.js b/packages/react/src/views/AttachmentHandler/TextAttachment.js index 73387c7413..1bdd9b602d 100644 --- a/packages/react/src/views/AttachmentHandler/TextAttachment.js +++ b/packages/react/src/views/AttachmentHandler/TextAttachment.js @@ -1,4 +1,5 @@ import React, { useState, useContext } from 'react'; +import { format } from 'date-fns'; import { css } from '@emotion/react'; import PropTypes from 'prop-types'; import { Box, Avatar, useTheme, Icon } from '@embeddedchat/ui-elements'; @@ -11,12 +12,11 @@ const FileAttachment = ({ attachment, host, type, - author, variantStyles = {}, msg, }) => { const { RCInstance } = useContext(RCContext); - const { theme } = useTheme(); + const { theme, mode } = useTheme(); const [isExpanded, setIsExpanded] = useState(true); const getUserAvatarUrl = (icon) => { @@ -28,6 +28,51 @@ const FileAttachment = ({ setIsExpanded((prevState) => !prevState); }; + const getMessageIdFromAttachment = (quoteAttachment) => { + const link = quoteAttachment?.title_link || quoteAttachment?.message_link; + if (!link) return null; + + try { + const query = link.includes('?') ? link.split('?')[1] : ''; + const params = new URLSearchParams(query); + return params.get('msg'); + } catch { + return null; + } + }; + + const handleQuoteClick = (quoteAttachment) => { + const msgId = getMessageIdFromAttachment(quoteAttachment); + if (!msgId) return; + + const element = document.getElementById(`ec-message-body-${msgId}`); + if (!element) return; + + const container = element.closest('.ec-message'); + if (!container) return; + + container.scrollIntoView({ behavior: 'smooth', block: 'center' }); + + const highlightColor = + mode === 'light' ? theme.colors.warningForeground : theme.colors.warning; + + container.style.transition = 'background-color 1s ease-out'; + container.style.backgroundColor = highlightColor; + + setTimeout(() => { + container.style.backgroundColor = ''; + }, 2000); + }; + + const getTimeString = (ts) => { + if (!ts) return null; + if (typeof ts === 'object' && ts.$date) { + ts = ts.$date; + } + const date = new Date(ts); + return !Number.isNaN(date.getTime()) ? format(date, 'h:mm a') : ts; + }; + const formatFileSize = (bytes) => { if (!bytes || bytes === 0) return '0 B'; @@ -44,9 +89,9 @@ const FileAttachment = ({ return `${size.toFixed(decimals)} ${units[unitIndex]}`; }; - const getFileSizeWithFormat = (size, format) => { + const getFileSizeWithFormat = (size, fileFormat) => { const formattedSize = formatFileSize(size); - return format ? `${formattedSize} - ${format}` : formattedSize; + return fileFormat ? `${formattedSize} - ${fileFormat}` : formattedSize; }; return ( @@ -84,6 +129,21 @@ const FileAttachment = ({ size="1.2em" /> @{attachment?.author_name} + {getTimeString(attachment?.ts) && ( + handleQuoteClick(attachment)} + css={css` + font-size: 0.75rem; + color: ${theme.colors.mutedForeground}; + cursor: pointer; + &:hover { + text-decoration: underline; + } + `} + > + {getTimeString(attachment.ts)} + + )} )} @@ -217,6 +277,21 @@ const FileAttachment = ({ size="1.2em" /> @{nestedAttachment?.author_name} + {getTimeString(nestedAttachment?.ts) && ( + handleQuoteClick(nestedAttachment)} + css={css` + font-size: 0.75rem; + color: ${theme.colors.mutedForeground}; + cursor: pointer; + &:hover { + text-decoration: underline; + } + `} + > + {getTimeString(nestedAttachment.ts)} + + )} )} @@ -309,7 +384,6 @@ FileAttachment.propTypes = { attachment: PropTypes.object, host: PropTypes.string, type: PropTypes.string, - author: PropTypes.object, variantStyles: PropTypes.object, msg: PropTypes.object, }; From 2ccf1901034becc39ca6e9cf2be4f4fdb8213ca6 Mon Sep 17 00:00:00 2001 From: srijna Date: Sat, 21 Mar 2026 11:11:20 +0530 Subject: [PATCH 125/153] feat: Added max length validation in file upload modal in file description (#1037) * feat: added max length validation in file uplode modal in file description * fetch message limit from RC * fixed lint format * implemented review points * fix: resolve prettier formatting in AttachmentPreview * remove comments * fix: remove empty catch block to resolve no-empty lint error Made-with: Cursor --- packages/react/src/store/settingsStore.js | 12 +- .../AttachmentPreview/AttachmentPreview.js | 169 ++++++++++++------ .../react/src/views/ChatHeader/ChatHeader.js | 9 +- 3 files changed, 129 insertions(+), 61 deletions(-) diff --git a/packages/react/src/store/settingsStore.js b/packages/react/src/store/settingsStore.js index 4f9d78b687..a8ba42e96b 100644 --- a/packages/react/src/store/settingsStore.js +++ b/packages/react/src/store/settingsStore.js @@ -1,8 +1,16 @@ import { create } from 'zustand'; +const DEFAULT_MESSAGE_LIMIT = 5000; + const useSettingsStore = create((set) => ({ - messageLimit: 5000, - setMessageLimit: (messageLimit) => set(() => ({ messageLimit })), + messageLimit: DEFAULT_MESSAGE_LIMIT, + setMessageLimit: (messageLimit) => + set(() => ({ + messageLimit: + typeof messageLimit === 'number' && Number.isFinite(messageLimit) + ? messageLimit + : DEFAULT_MESSAGE_LIMIT, + })), })); export default useSettingsStore; diff --git a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js index 01123dafbe..e6dadeae61 100644 --- a/packages/react/src/views/AttachmentPreview/AttachmentPreview.js +++ b/packages/react/src/views/AttachmentPreview/AttachmentPreview.js @@ -1,10 +1,18 @@ import React, { useContext, useState, useRef, useEffect } from 'react'; import { css } from '@emotion/react'; -import { Box, Icon, Button, Input, Modal } from '@embeddedchat/ui-elements'; +import { + Box, + Icon, + Button, + Input, + Modal, + useTheme, +} from '@embeddedchat/ui-elements'; import useAttachmentWindowStore from '../../store/attachmentwindow'; import CheckPreviewType from './CheckPreviewType'; import RCContext from '../../context/RCInstance'; import { useMessageStore, useMemberStore } from '../../store'; +import useSettingsStore from '../../store/settingsStore'; import getAttachmentPreviewStyles from './AttachmentPreview.styles'; import { parseEmoji } from '../../lib/emoji'; import MembersList from '../Mentions/MembersList'; @@ -13,29 +21,30 @@ import useSearchMentionUser from '../../hooks/useSearchMentionUser'; const AttachmentPreview = () => { const { RCInstance, ECOptions } = useContext(RCContext); + const { theme } = useTheme(); const styles = getAttachmentPreviewStyles(); const toggle = useAttachmentWindowStore((state) => state.toggle); const data = useAttachmentWindowStore((state) => state.data); const setData = useAttachmentWindowStore((state) => state.setData); + const [isPending, setIsPending] = useState(false); const messageRef = useRef(null); const [showMembersList, setShowMembersList] = useState(false); const [filteredMembers, setFilteredMembers] = useState([]); const [mentionIndex, setMentionIndex] = useState(-1); const [startReadMentionUser, setStartReadMentionUser] = useState(false); - const [keyPressed, setKeyPressed] = useState(null); - const [fileName, setFileName] = useState(data?.name); + const [fileName, setFileName] = useState(data?.name ?? ''); + useEffect(() => setFileName(data?.name ?? ''), [data?.name]); - const threadId = useMessageStore((state) => state.threadMainMessage?._id); - const handleFileName = (e) => { - setFileName(e.target.value); - }; + const [description, setDescription] = useState(''); + const charCount = description.length; + const msgMaxLength = useSettingsStore((s) => s?.messageLimit); + const isOverLimit = msgMaxLength && charCount > msgMaxLength; - const { members } = useMemberStore((state) => ({ - members: state.members, - })); + const threadId = useMessageStore((state) => state.threadMainMessage?._id); + const { members } = useMemberStore((state) => ({ members: state.members })); const searchMentionUser = useSearchMentionUser( members, @@ -46,47 +55,44 @@ const AttachmentPreview = () => { setShowMembersList ); + const handleFileName = (e) => setFileName(e.target.value); + const handleFileDescription = (e) => { - const description = e.target.value; - messageRef.current.value = parseEmoji(description); - searchMentionUser(description); + const raw = e.target.value || ''; + setDescription(raw); + + if (messageRef.current && typeof messageRef.current.value !== 'undefined') { + messageRef.current.value = raw; + } + + searchMentionUser(raw); }; const submit = async () => { + if (isPending) return; + if (msgMaxLength && description.length > msgMaxLength) return; + setIsPending(true); - await RCInstance.sendAttachment( - data, - fileName, - messageRef.current.value, - ECOptions?.enableThreads ? threadId : undefined - ); - toggle(); - setData(null); - if (isPending) { + try { + await RCInstance.sendAttachment( + data, + fileName, + parseEmoji(description), + ECOptions?.enableThreads ? threadId : undefined + ); + toggle(); + setData(null); + } finally { setIsPending(false); } }; - useEffect(() => { - const keyHandler = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - setKeyPressed('Enter'); - } - }; - - document.addEventListener('keydown', keyHandler); - return () => { - document.removeEventListener('keydown', keyHandler); - }; - }, []); - - useEffect(() => { - if (keyPressed === 'Enter') { + const onDescKeyDown = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); submit(); - setKeyPressed(null); } - }, [keyPressed, submit]); + }; return ( @@ -98,11 +104,12 @@ const AttachmentPreview = () => { css={css` margin-right: 0.5rem; `} - />{' '} + /> File Upload + { > + { File name { - handleFileName(e); - }} + onChange={handleFileName} value={fileName} type="text" css={styles.input} @@ -150,6 +156,7 @@ const AttachmentPreview = () => { > File description + {showMembersList && ( @@ -161,21 +168,73 @@ const AttachmentPreview = () => { setFilteredMembers={setFilteredMembers} setStartReadMentionUser={setStartReadMentionUser} setShowMembersList={setShowMembersList} - css={css` - width: auto; - `} /> )} + { - handleFileDescription(e); - }} + onChange={handleFileDescription} + onKeyDown={onDescKeyDown} type="text" - css={styles.input} placeholder="Description" ref={messageRef} + value={description} + css={css` + ${styles.input}; + border-color: ${isOverLimit + ? theme.colors.destructive + : null}; + color: ${isOverLimit ? theme.colors.destructive : null}; + `} /> + + {msgMaxLength && ( + + + {isOverLimit + ? `Cannot upload file, description is over the ${msgMaxLength} character limit` + : ''} + + + + + )} @@ -190,12 +249,8 @@ const AttachmentPreview = () => { - diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index 0986104ae5..a310e538fd 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -150,8 +150,13 @@ const ChatHeader = ({ useEffect(() => { const getMessageLimit = async () => { - const messageLimitObj = await RCInstance.getMessageLimit(); - setMessageLimit(messageLimitObj?.value); + try { + const messageLimitObj = await RCInstance.getMessageLimit(); + setMessageLimit(messageLimitObj?.value); + } catch (e) { + console.error('Failed to fetch message limit', e); + setMessageLimit(undefined); + } }; const setMessageAllowed = async () => { From 46546f3c30cdf97afd10a3e42df63ed6bf4fa4d1 Mon Sep 17 00:00:00 2001 From: Kartik Date: Sat, 21 Mar 2026 11:36:16 +0530 Subject: [PATCH 126/153] fix: update file upload to use RC 8.x rooms.media API (#1070) * fix: update file upload to use RC 8.x rooms.media API - Replace deprecated rooms.upload endpoint with new two-step process - Step 1: POST to rooms.media/:rid to upload file - Step 2: POST to rooms.mediaConfirm/:rid/:fileId to confirm with metadata - Fixes file upload 404 error on Rocket.Chat 8.x servers * fix: reset file input after upload to allow re-selecting same file * style: fix prettier formatting * fix: resolve variable shadowing in ChatInput.js * Remove comments * Remove comments --------- Co-authored-by: dodaa08 --- packages/api/src/EmbeddedChatApi.ts | 59 ++++++++++++++----- .../react/src/views/ChatInput/ChatInput.js | 11 +++- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 72e25a0466..a3549af990 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -1056,26 +1056,53 @@ export default class EmbeddedChatApi { ) { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const form = new FormData(); - if (threadId) { - form.append("tmid", threadId); + if (!userId || !authToken) { + console.error("sendAttachment: User not authenticated"); + return; } + + const form = new FormData(); form.append("file", file, fileName); - form.append( - "description", - fileDescription.length !== 0 ? fileDescription : "" + + const uploadResponse = await fetch( + `${this.host}/api/v1/rooms.media/${this.rid}`, + { + method: "POST", + body: form, + headers: { + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + } ); - const response = fetch(`${this.host}/api/v1/rooms.upload/${this.rid}`, { - method: "POST", - body: form, - headers: { - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - }).then((r) => r.json()); - return response; + + const uploadResult = await uploadResponse.json(); + + if (!uploadResult.success || !uploadResult.file?._id) { + console.error("sendAttachment: Upload failed", uploadResult); + return uploadResult; + } + + const confirmResponse = await fetch( + `${this.host}/api/v1/rooms.mediaConfirm/${this.rid}/${uploadResult.file._id}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": authToken, + "X-User-Id": userId, + }, + body: JSON.stringify( + threadId + ? { msg: "", description: fileDescription || "", tmid: threadId } + : { msg: "", description: fileDescription || "" } + ), + } + ); + + return await confirmResponse.json(); } catch (err) { - console.log(err); + console.error("sendAttachment error:", err); } } diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index e753b689ae..0bdebf322b 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -120,9 +120,10 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { ); const isLoginIn = useLoginStore((state) => state.isLoginIn); - const { toggle, setData } = useAttachmentWindowStore((state) => ({ + const { toggle, setData, data } = useAttachmentWindowStore((state) => ({ toggle: state.toggle, setData: state.setData, + data: state.data, })); const userInfo = { _id: userId, username, name }; @@ -147,7 +148,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { RCInstance.auth.onAuthChange((user) => { if (user) { RCInstance.getCommandsList() - .then((data) => setCommands(data.commands || [])) + .then((response) => setCommands(response.commands || [])) .catch(console.error); RCInstance.getChannelMembers(isChannelPrivate) @@ -184,6 +185,12 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { } }, [deletedMessage]); + useEffect(() => { + if (data === null && inputRef.current) { + inputRef.current.value = ''; + } + }, [data]); + const getMessageLink = async (id) => { const host = RCInstance.getHost(); const res = await RCInstance.channelInfo(); From 631be797d065fd6681815f26c909eab6166312b6 Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:48:42 +0530 Subject: [PATCH 127/153] fix: resolve memory leak in MediaRecorder event listeners (#1077) --- packages/react/src/hooks/useMediaRecorder.js | 42 ++++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/react/src/hooks/useMediaRecorder.js b/packages/react/src/hooks/useMediaRecorder.js index 028b0d9b1d..1c79fe20b3 100644 --- a/packages/react/src/hooks/useMediaRecorder.js +++ b/packages/react/src/hooks/useMediaRecorder.js @@ -28,19 +28,29 @@ export function useMediaRecorder({ constraints, onStop, videoRef }) { const stream = await getStream(constraints, true); chunks.current = []; const _recorder = new MediaRecorder(stream); - _recorder.start(); - setRecorder(_recorder); - _recorder.addEventListener('dataavailable', (event) => { + + const handleDataAvailable = (event) => { chunks.current.push(event.data); - }); - _recorder.addEventListener('stop', () => { + }; + + const handleStop = () => { onStop && onStop(chunks.current); - }); + _recorder.removeEventListener('dataavailable', handleDataAvailable); + _recorder.removeEventListener('stop', handleStop); + }; + + _recorder.addEventListener('dataavailable', handleDataAvailable); + _recorder.addEventListener('stop', handleStop); + + _recorder.start(); + setRecorder(_recorder); } async function stop() { if (recorder) { - recorder.stop(); + if (recorder.state !== 'inactive') { + recorder.stop(); + } (await getStream()).getTracks().forEach((track) => track.stop()); } } @@ -77,17 +87,23 @@ export function useNewMediaRecorder({ constraints, videoRef, onStop }) { chunks.current = []; const _recorder = new MediaRecorder(stream); - _recorder.start(); - setRecorder(_recorder); - _recorder.addEventListener('dataavailable', (event) => { + const handleDataAvailable = (event) => { chunks.current.push(event.data); - }); + }; - _recorder.addEventListener('stop', () => { + const handleStop = () => { setRecordingData(new Blob(chunks.current, { type: 'video/mp4' })); onStop && onStop(chunks.current); - }); + _recorder.removeEventListener('dataavailable', handleDataAvailable); + _recorder.removeEventListener('stop', handleStop); + }; + + _recorder.addEventListener('dataavailable', handleDataAvailable); + _recorder.addEventListener('stop', handleStop); + + _recorder.start(); + setRecorder(_recorder); } async function stopRecording() { From 039a3c9a07925697b4bd6032c04b3a83340fb615 Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:55:48 +0530 Subject: [PATCH 128/153] fix: show edit icon for sequential messages (#1079) * fix: show edit icon for sequential messages * style: fix formatting in MessageAvatarContainer --- packages/react/src/views/Message/Message.styles.js | 6 ++++++ packages/react/src/views/Message/MessageAvatarContainer.js | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/packages/react/src/views/Message/Message.styles.js b/packages/react/src/views/Message/Message.styles.js index 5cdf333662..03236addbd 100644 --- a/packages/react/src/views/Message/Message.styles.js +++ b/packages/react/src/views/Message/Message.styles.js @@ -230,6 +230,12 @@ export const getMessageHeaderStyles = (theme) => { flex-shrink: 0; margin-left: 0.25rem; `, + messageStatus: css` + display: flex; + flex-flow: row nowrap; + align-items: center; + margin-left: 0.2rem; + `, }; return styles; diff --git a/packages/react/src/views/Message/MessageAvatarContainer.js b/packages/react/src/views/Message/MessageAvatarContainer.js index 016bbb0b06..7fdf1b0b8a 100644 --- a/packages/react/src/views/Message/MessageAvatarContainer.js +++ b/packages/react/src/views/Message/MessageAvatarContainer.js @@ -60,6 +60,11 @@ const MessageAvatarContainer = ({ ) : null} + {message.editedAt && sequential ? ( + + + + ) : null} {isPinned && sequential ? ( From 37896cb04db6b836d5524026b60e3f25f77c47bc Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:58:42 +0530 Subject: [PATCH 129/153] fix(auth): validate origin of oauth callback to prevent login csrf (#1093) --- packages/auth/src/loginWithRocketChatOAuth.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/auth/src/loginWithRocketChatOAuth.ts b/packages/auth/src/loginWithRocketChatOAuth.ts index 955e70c7a4..66a553a9f1 100644 --- a/packages/auth/src/loginWithRocketChatOAuth.ts +++ b/packages/auth/src/loginWithRocketChatOAuth.ts @@ -45,6 +45,9 @@ width=800,height=600,left=-1000,top=-1000,rel=opener`; return new Promise((resolve) => { if (popup) { const onMessage = async (e: MessageEvent) => { + if (e.origin !== new URL(config.api.baseUrl).origin) { + return; + } if (e.data.type === "rc-oauth-callback") { const { accessToken, expiresIn, serviceName } = e.data.credentials; const response = await config.api.post("/api/v1/login", { From d3e6ee731ad313d1f601b65d28bf527b90fb9176 Mon Sep 17 00:00:00 2001 From: "khizar (RinX)" <109973520+Khizarshah01@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:13:48 +0530 Subject: [PATCH 130/153] fix: correctly render nested quotes media attachments in quotes (#1075) * fix: correctly render nested media attachments in quotes * chore: fix formatting in TextAttachment --- .../AttachmentHandler/AttachmentMetadata.js | 28 +- .../views/AttachmentHandler/TextAttachment.js | 343 ++++++++++++------ 2 files changed, 238 insertions(+), 133 deletions(-) diff --git a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js index 0c4eda27aa..a3a4076f36 100644 --- a/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js +++ b/packages/react/src/views/AttachmentHandler/AttachmentMetadata.js @@ -41,13 +41,15 @@ const AttachmentMetadata = ({ } else if (attachment?.size) { sizeInBytes = attachment.size; } else { - sizeInBytes = 0; + return null; } const sizeInKB = (sizeInBytes / 1024).toFixed(2); return `${sizeInKB} kB`; }; + const fileSize = getFormattedFileSize(); + return ( - - ({getFormattedFileSize()}) - + {fileSize && ( + + ({fileSize}) + + )} ( - + attachment.attachments.map((nestedAttachment, index) => { + if (nestedAttachment?.audio_url) { + return ( + + ); + } + if (nestedAttachment?.video_url) { + return ( + + ); + } + if (nestedAttachment?.image_url) { + return ( + + ); + } + // Check for wrapped attachments (mirroring Attachment.js logic) + if (nestedAttachment?.attachments?.[0]?.audio_url) { + return ( + + ); + } + if (nestedAttachment?.attachments?.[0]?.video_url) { + return ( + + ); + } + if (nestedAttachment?.attachments?.[0]?.image_url) { + return ( + + ); + } + + return ( - {nestedAttachment?.author_name && ( - <> - - @{nestedAttachment?.author_name} - {getTimeString(nestedAttachment?.ts) && ( - handleQuoteClick(nestedAttachment)} - css={css` - font-size: 0.75rem; - color: ${theme.colors.mutedForeground}; - cursor: pointer; - &:hover { - text-decoration: underline; - } - `} - > - {getTimeString(nestedAttachment.ts)} - - )} - - )} - - - - - {isExpanded && ( - {nestedAttachment?.text ? ( - nestedAttachment.text[0] === '[' ? ( - nestedAttachment.text.match(/\n(.*)/)?.[1] || '' - ) : ( - + - ) - ) : ( - - - - @{nestedAttachment?.author_name} + {getTimeString(nestedAttachment?.ts) && ( + handleQuoteClick(nestedAttachment)} css={css` - text-decoration: none; - font-size: 0.875rem; + font-size: 0.75rem; + color: ${theme.colors.mutedForeground}; + cursor: pointer; &:hover { text-decoration: underline; } `} > - {nestedAttachment?.title} - + {getTimeString(nestedAttachment.ts)} + + )} + + )} + + + {!nestedAttachment?.text && !nestedAttachment?.attachments && ( + + )} + + {isExpanded && ( + + {nestedAttachment?.text ? ( + nestedAttachment.text[0] === '[' ? ( + nestedAttachment.text.match(/\n(.*)/)?.[1] || '' + ) : ( + + ) + ) : ( + + - {getFileSizeWithFormat( - nestedAttachment?.size, - nestedAttachment?.format - )} + + {nestedAttachment?.title} + + + {getFileSizeWithFormat( + nestedAttachment?.size, + nestedAttachment?.format + )} + - - )} - - )} - - ))} + )} + + )} + + ); + })} ); From 10c86efa68523e0c819b914aa341ea977b093d80 Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:15:14 +0530 Subject: [PATCH 131/153] Initial fix logic (#1085) --- .../src/components/ToastBar/ToastBar.js | 12 +++++++-- .../components/ToastBar/ToastBar.styles.js | 4 ++- .../components/ToastBar/ToastBarProvider.js | 6 ++++- .../src/components/ToastBar/ToastContainer.js | 27 ++++++++++++------- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/packages/ui-elements/src/components/ToastBar/ToastBar.js b/packages/ui-elements/src/components/ToastBar/ToastBar.js index 71ea53a9d8..dcec7d6f5f 100644 --- a/packages/ui-elements/src/components/ToastBar/ToastBar.js +++ b/packages/ui-elements/src/components/ToastBar/ToastBar.js @@ -11,6 +11,7 @@ import useTheme from '../../hooks/useTheme'; const ToastBar = ({ toast, onClose }) => { const { type, message, time = 2000 } = toast; const toastRef = useRef(); + const latestOnClose = useRef(onClose); const { theme } = useTheme(); const { classNames, styleOverrides } = useComponentOverrides('ToastBar'); @@ -42,8 +43,15 @@ const ToastBar = ({ toast, onClose }) => { }, [theme.colors, type]); useEffect(() => { - setTimeout(onClose, time); - }, [onClose, time]); + latestOnClose.current = onClose; + }, [onClose]); + + useEffect(() => { + const timer = setTimeout(() => { + latestOnClose.current?.(); + }, time); + return () => clearTimeout(timer); + }, [time]); return ( { position: absolute; z-index: ${theme.zIndex?.toastbar || 1600}; border-radius: ${theme.radius}; - animation: ${animation} ${2000}ms ease-in-out forwards; + display: flex; + flex-direction: column; + gap: 0.75rem; `, }; return styles; diff --git a/packages/ui-elements/src/components/ToastBar/ToastBarProvider.js b/packages/ui-elements/src/components/ToastBar/ToastBarProvider.js index c3507ad61a..1455351ee9 100644 --- a/packages/ui-elements/src/components/ToastBar/ToastBarProvider.js +++ b/packages/ui-elements/src/components/ToastBar/ToastBarProvider.js @@ -6,7 +6,11 @@ const ToastBarProvider = ({ position = 'bottom right', children }) => { const [toasts, setToasts] = useState([]); const dispatchToast = useCallback( (toast) => { - setToasts((prevToasts) => [toast, ...prevToasts]); + const withId = { + id: toast.id || `${Date.now()}-${Math.random().toString(36).slice(2)}`, + ...toast, + }; + setToasts((prevToasts) => [withId, ...prevToasts]); }, [setToasts] ); diff --git a/packages/ui-elements/src/components/ToastBar/ToastContainer.js b/packages/ui-elements/src/components/ToastBar/ToastContainer.js index d417b6d67f..a382444a39 100644 --- a/packages/ui-elements/src/components/ToastBar/ToastContainer.js +++ b/packages/ui-elements/src/components/ToastBar/ToastContainer.js @@ -9,6 +9,7 @@ const ToastContainer = () => { const { theme } = useTheme(); const styles = getToastBarContainerStyles(theme); const { position, toasts, setToasts } = useContext(ToastContext); + const positionStyle = useMemo(() => { const positions = position.split(/\s+/); const styleAnchor = {}; @@ -17,21 +18,29 @@ const ToastContainer = () => { }); return styleAnchor; }, [position]); - const currentToast = useMemo(() => { - const toast = toasts[toasts.length - 1]; - return toast; - }, [toasts]); - const onClose = useCallback(() => { - setToasts(toasts.slice(0, toasts.length - 1)); - }, [setToasts, toasts]); - if (!currentToast) { + const stackedToasts = useMemo(() => [...toasts].reverse(), [toasts]); + + const handleClose = useCallback( + (id) => { + setToasts((prevItems) => prevItems.filter((toast) => toast.id !== id)); + }, + [setToasts] + ); + + if (!stackedToasts.length) { return null; } return ( - + {stackedToasts.map((toast) => ( + handleClose(toast.id)} + /> + ))} ); }; From 7734fd0e85b903bbb6420359b0b5c05fcf4c0fc0 Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:17:38 +0530 Subject: [PATCH 132/153] Intial logic for tooltip label position (#1087) --- .../layout_editor/src/components/SurfaceMenu/SurfaceItem.jsx | 2 +- packages/react/src/views/SurfaceMenu/SurfaceItem.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/layout_editor/src/components/SurfaceMenu/SurfaceItem.jsx b/packages/layout_editor/src/components/SurfaceMenu/SurfaceItem.jsx index 4a4f47f231..caeb77ec37 100644 --- a/packages/layout_editor/src/components/SurfaceMenu/SurfaceItem.jsx +++ b/packages/layout_editor/src/components/SurfaceMenu/SurfaceItem.jsx @@ -18,7 +18,7 @@ const SurfaceItem = ({ onRemove, type, cursor = 'grab', - tooltipPosition = 'bottom', + tooltipPosition = 'top', size, }) => { const { attributes, listeners, setNodeRef, transform, isDragging } = diff --git a/packages/react/src/views/SurfaceMenu/SurfaceItem.js b/packages/react/src/views/SurfaceMenu/SurfaceItem.js index 9674ead1cf..f9f061b8b8 100644 --- a/packages/react/src/views/SurfaceMenu/SurfaceItem.js +++ b/packages/react/src/views/SurfaceMenu/SurfaceItem.js @@ -2,7 +2,7 @@ import React from 'react'; import { Tooltip, ActionButton } from '@embeddedchat/ui-elements'; const SurfaceItem = ({ item, size }) => ( - + Date: Sat, 21 Mar 2026 12:20:20 +0530 Subject: [PATCH 133/153] feat: Add Emoji Autocomplete functionality that displays matching emoji suggestions on : colon type (#1015) * Add emoji autocomplete functionality that displays matching emoji suggestions when users type ':' followed by at least 2 characters, similar to existing mention and command autocomplete features * Theme refinement * Prettier formatting #1 * Prettier Formatting #2 --- packages/react/src/hooks/useSearchEmoji.js | 67 +++ packages/react/src/lib/emojiList.js | 464 ++++++++++++++++++ .../react/src/views/ChatInput/ChatInput.js | 35 +- .../react/src/views/EmojiList/EmojiList.js | 162 ++++++ .../src/views/EmojiList/EmojiList.styles.js | 53 ++ packages/react/src/views/EmojiList/index.js | 1 + 6 files changed, 780 insertions(+), 2 deletions(-) create mode 100644 packages/react/src/hooks/useSearchEmoji.js create mode 100644 packages/react/src/lib/emojiList.js create mode 100644 packages/react/src/views/EmojiList/EmojiList.js create mode 100644 packages/react/src/views/EmojiList/EmojiList.styles.js create mode 100644 packages/react/src/views/EmojiList/index.js diff --git a/packages/react/src/hooks/useSearchEmoji.js b/packages/react/src/hooks/useSearchEmoji.js new file mode 100644 index 0000000000..7112fc3d81 --- /dev/null +++ b/packages/react/src/hooks/useSearchEmoji.js @@ -0,0 +1,67 @@ +import { useCallback } from 'react'; +import emojiList from '../lib/emojiList'; + +const useSearchEmoji = ( + startReadEmoji, + setStartReadEmoji, + setFilteredEmojis, + setEmojiIndex, + setShowEmojiList +) => + useCallback( + (message) => { + const lastChar = message ? message[message.length - 1] : ''; + + if (message.length === 0) { + setShowEmojiList(false); + setStartReadEmoji(false); + setFilteredEmojis([]); + setEmojiIndex(-1); + return; + } + + // Check if user is typing emoji syntax (:emoji:) + const emojiMatch = message.match(/:([a-zA-Z0-9_+-]*?)$/); + + if (emojiMatch) { + const query = emojiMatch[1].toLowerCase(); + + // Only show suggestions if query is at least 2 characters + if (query.length >= 2) { + setStartReadEmoji(true); + + const filteredEmojis = emojiList + .filter( + (emoji) => + emoji.shortname.toLowerCase().includes(query) || + emoji.aliases.some((alias) => + alias.toLowerCase().includes(query) + ) + ) + .slice(0, 10); + + setFilteredEmojis(filteredEmojis); + setEmojiIndex(filteredEmojis.length > 0 ? 0 : -1); + setShowEmojiList(filteredEmojis.length > 0); + } else { + setShowEmojiList(false); + setFilteredEmojis([]); + setEmojiIndex(-1); + } + } else if (startReadEmoji) { + setStartReadEmoji(false); + setFilteredEmojis([]); + setEmojiIndex(-1); + setShowEmojiList(false); + } + }, + [ + startReadEmoji, + setStartReadEmoji, + setFilteredEmojis, + setEmojiIndex, + setShowEmojiList, + ] + ); + +export default useSearchEmoji; diff --git a/packages/react/src/lib/emojiList.js b/packages/react/src/lib/emojiList.js new file mode 100644 index 0000000000..4aa794a7a0 --- /dev/null +++ b/packages/react/src/lib/emojiList.js @@ -0,0 +1,464 @@ +// Comprehensive list of popular emojis with emoji-toolkit compatible shortnames +// Based on the most commonly used emojis and matching Rocket.Chat's emoji autocomplete + +const emojiList = [ + // Popular smileys and emotions + { shortname: 'smile', emoji: '😊', aliases: ['happy', 'grinning'] }, + { shortname: 'laughing', emoji: '😂', aliases: ['joy', 'tears'] }, + { shortname: 'heart_eyes', emoji: '😍', aliases: ['love', 'heart'] }, + { shortname: 'kissing_heart', emoji: '😘', aliases: ['kiss', 'love'] }, + { shortname: 'wink', emoji: '😉', aliases: ['winking'] }, + { shortname: 'blush', emoji: '😊', aliases: ['smiling', 'happy'] }, + { shortname: 'grinning', emoji: '😀', aliases: ['grin', 'happy'] }, + { shortname: 'grin', emoji: '😁', aliases: ['grinning', 'happy'] }, + { shortname: 'joy', emoji: '😂', aliases: ['laughing', 'tears'] }, + { shortname: 'smiley', emoji: '😃', aliases: ['happy', 'grinning'] }, + { shortname: 'smirk', emoji: '😏', aliases: ['smirking'] }, + { shortname: 'relaxed', emoji: '☺️', aliases: ['smile', 'happy'] }, + { + shortname: 'stuck_out_tongue', + emoji: '😛', + aliases: ['tongue', 'playful'], + }, + { + shortname: 'stuck_out_tongue_winking_eye', + emoji: '😜', + aliases: ['wink', 'tongue'], + }, + { + shortname: 'stuck_out_tongue_closed_eyes', + emoji: '😝', + aliases: ['tongue', 'playful'], + }, + { shortname: 'kissing', emoji: '😗', aliases: ['kiss'] }, + { + shortname: 'kissing_smiling_eyes', + emoji: '😙', + aliases: ['kiss', 'happy'], + }, + { shortname: 'kissing_closed_eyes', emoji: '😚', aliases: ['kiss', 'love'] }, + { shortname: 'yum', emoji: '😋', aliases: ['delicious', 'tasty'] }, + { shortname: 'relieved', emoji: '😌', aliases: ['relief', 'sigh'] }, + { shortname: 'hugs', emoji: '🤗', aliases: ['hugging'] }, + { shortname: 'thinking', emoji: '🤔', aliases: ['thought', 'considering'] }, + { shortname: 'neutral_face', emoji: '😐', aliases: ['neutral', 'meh'] }, + { shortname: 'expressionless', emoji: '😑', aliases: ['blank', 'neutral'] }, + { shortname: 'no_mouth', emoji: '😶', aliases: ['mute', 'silent'] }, + { shortname: 'rolling_eyes', emoji: '🙄', aliases: ['eyeroll', 'sarcasm'] }, + { shortname: 'smiling_imp', emoji: '😈', aliases: ['devil', 'evil'] }, + { shortname: 'imp', emoji: '👿', aliases: ['devil', 'evil'] }, + { shortname: 'japanese_ogre', emoji: '👹', aliases: ['monster', 'ogre'] }, + { shortname: 'japanese_goblin', emoji: '👺', aliases: ['goblin', 'monster'] }, + { shortname: 'skull', emoji: '💀', aliases: ['death', 'dead'] }, + { shortname: 'ghost', emoji: '👻', aliases: ['spooky', 'halloween'] }, + { shortname: 'alien', emoji: '👽', aliases: ['ufo', 'extraterrestrial'] }, + { shortname: 'robot', emoji: '🤖', aliases: ['bot', 'automation'] }, + { shortname: 'smiley_cat', emoji: '😸', aliases: ['cat', 'happy'] }, + { shortname: 'smile_cat', emoji: '😹', aliases: ['cat', 'laughing'] }, + { shortname: 'joy_cat', emoji: '😻', aliases: ['cat', 'love'] }, + { shortname: 'heart_eyes_cat', emoji: '😻', aliases: ['cat', 'love'] }, + { shortname: 'smirk_cat', emoji: '😼', aliases: ['cat', 'smirk'] }, + { shortname: 'kissing_cat', emoji: '😽', aliases: ['cat', 'kiss'] }, + { shortname: 'scream_cat', emoji: '🙀', aliases: ['cat', 'scared'] }, + { shortname: 'crying_cat_face', emoji: '😿', aliases: ['cat', 'cry', 'sad'] }, + { shortname: 'pouting_cat', emoji: '😾', aliases: ['cat', 'angry'] }, + + // Hand gestures + { shortname: 'thumbsup', emoji: '👍', aliases: ['yes', 'good', 'approve'] }, + { + shortname: 'thumbsdown', + emoji: '👎', + aliases: ['no', 'bad', 'disapprove'], + }, + { shortname: 'clap', emoji: '👏', aliases: ['applause', 'bravo'] }, + { shortname: 'open_hands', emoji: '👐', aliases: ['open', 'hands'] }, + { shortname: 'palms_up_together', emoji: '🤲', aliases: ['prayer', 'hands'] }, + { shortname: 'handshake', emoji: '🤝', aliases: ['deal', 'agreement'] }, + { shortname: 'pray', emoji: '🙏', aliases: ['please', 'thanks', 'hope'] }, + { shortname: 'writing_hand', emoji: '✍️', aliases: ['write', 'signature'] }, + { shortname: 'nail_care', emoji: '💅', aliases: ['nail', 'polish'] }, + { shortname: 'selfie', emoji: '🤳', aliases: ['camera', 'photo'] }, + { shortname: 'muscle', emoji: '💪', aliases: ['strong', 'flex', 'biceps'] }, + { shortname: 'mechanical_arm', emoji: '🦾', aliases: ['arm', 'robot'] }, + { shortname: 'mechanical_leg', emoji: '🦿', aliases: ['leg', 'robot'] }, + { shortname: 'leg', emoji: '🦵', aliases: ['kick', 'limb'] }, + { shortname: 'foot', emoji: '🦶', aliases: ['kick', 'limb'] }, + { shortname: 'ear', emoji: '👂', aliases: ['listen', 'hear'] }, + { + shortname: 'ear_with_hearing_aid', + emoji: '🦻', + aliases: ['hearing', 'aid'], + }, + { shortname: 'nose', emoji: '👃', aliases: ['smell', 'sniff'] }, + { shortname: 'brain', emoji: '🧠', aliases: ['think', 'smart'] }, + { shortname: 'anatomical_heart', emoji: '🫀', aliases: ['heart', 'health'] }, + { shortname: 'lungs', emoji: '🫁', aliases: ['breath', 'health'] }, + { shortname: 'tooth', emoji: '🦷', aliases: ['dental', 'health'] }, + { shortname: 'bone', emoji: '🦴', aliases: ['skeleton', 'health'] }, + { shortname: 'eyes', emoji: '👀', aliases: ['see', 'look', 'watch'] }, + { shortname: 'eye', emoji: '👁️', aliases: ['see', 'look'] }, + { shortname: 'tongue', emoji: '👅', aliases: ['taste', 'lick'] }, + { shortname: 'lips', emoji: '👄', aliases: ['kiss', 'mouth'] }, + + // Hearts and symbols + { shortname: 'heart', emoji: '❤️', aliases: ['love', 'red'] }, + { shortname: 'orange_heart', emoji: '🧡', aliases: ['love', 'orange'] }, + { shortname: 'yellow_heart', emoji: '💛', aliases: ['love', 'yellow'] }, + { shortname: 'green_heart', emoji: '💚', aliases: ['love', 'green'] }, + { shortname: 'blue_heart', emoji: '💙', aliases: ['love', 'blue'] }, + { shortname: 'purple_heart', emoji: '💜', aliases: ['love', 'purple'] }, + { shortname: 'black_heart', emoji: '🖤', aliases: ['love', 'black'] }, + { shortname: 'white_heart', emoji: '🤍', aliases: ['love', 'white'] }, + { shortname: 'brown_heart', emoji: '🤎', aliases: ['love', 'brown'] }, + { shortname: 'broken_heart', emoji: '💔', aliases: ['sad', 'broken'] }, + { + shortname: 'heart_exclamation', + emoji: '❣️', + aliases: ['love', 'exclamation'], + }, + { shortname: 'two_hearts', emoji: '💕', aliases: ['love', 'two'] }, + { + shortname: 'revolving_hearts', + emoji: '💞', + aliases: ['love', 'revolving'], + }, + { shortname: 'heartbeat', emoji: '💓', aliases: ['love', 'beat'] }, + { shortname: 'heartpulse', emoji: '💗', aliases: ['love', 'pulse'] }, + { shortname: 'heart_eyes', emoji: '😍', aliases: ['love', 'heart'] }, + { shortname: 'sparkling_heart', emoji: '💖', aliases: ['love', 'sparkle'] }, + { shortname: 'cupid', emoji: '💘', aliases: ['love', 'arrow'] }, + { shortname: 'gift_heart', emoji: '💝', aliases: ['love', 'gift'] }, + { + shortname: 'heart_decoration', + emoji: '💟', + aliases: ['love', 'decoration'], + }, + { shortname: 'peace_symbol', emoji: '☮️', aliases: ['peace', 'symbol'] }, + { shortname: 'latin_cross', emoji: '✝️', aliases: ['cross', 'christian'] }, + { shortname: 'star_and_crescent', emoji: '☪️', aliases: ['islam', 'muslim'] }, + { shortname: 'om', emoji: '🕉️', aliases: ['hindu', 'om'] }, + { + shortname: 'wheel_of_dharma', + emoji: '☸️', + aliases: ['buddhism', 'dharma'], + }, + { shortname: 'star_of_david', emoji: '✡️', aliases: ['judaism', 'star'] }, + { shortname: 'six_pointed_star', emoji: '🔯', aliases: ['star', 'jewish'] }, + { shortname: 'menorah', emoji: '🕎', aliases: ['hanukkah', 'candles'] }, + { shortname: 'yin_yang', emoji: '☯️', aliases: ['balance', 'tao'] }, + { shortname: 'orthodox_cross', emoji: '☦️', aliases: ['christian', 'cross'] }, + { + shortname: 'place_of_worship', + emoji: '🛐', + aliases: ['worship', 'religion'], + }, + { shortname: 'ophiuchus', emoji: '⛎', aliases: ['snake', 'zodiac'] }, + { shortname: 'aries', emoji: '♈', aliases: ['ram', 'zodiac'] }, + { shortname: 'taurus', emoji: '♉', aliases: ['bull', 'zodiac'] }, + { shortname: 'gemini', emoji: '♊', aliases: ['twins', 'zodiac'] }, + { shortname: 'cancer', emoji: '♋', aliases: ['crab', 'zodiac'] }, + { shortname: 'leo', emoji: '♌', aliases: ['lion', 'zodiac'] }, + { shortname: 'virgo', emoji: '♍', aliases: ['maiden', 'zodiac'] }, + { shortname: 'libra', emoji: '♎', aliases: ['scales', 'zodiac'] }, + { shortname: 'scorpius', emoji: '♏', aliases: ['scorpion', 'zodiac'] }, + { shortname: 'sagittarius', emoji: '♐', aliases: ['archer', 'zodiac'] }, + { shortname: 'capricorn', emoji: '♑', aliases: ['goat', 'zodiac'] }, + { shortname: 'aquarius', emoji: '♒', aliases: ['water', 'zodiac'] }, + { shortname: 'pisces', emoji: '♓', aliases: ['fish', 'zodiac'] }, + { shortname: 'id', emoji: '🆔', aliases: ['identification', 'id'] }, + { shortname: 'atom_symbol', emoji: '⚛️', aliases: ['atom', 'science'] }, + { shortname: 'u7a7a', emoji: '🈳', aliases: ['japanese', 'empty'] }, + { shortname: 'u5272', emoji: '🈹', aliases: ['japanese', 'discount'] }, + { + shortname: 'radioactive', + emoji: '☢️', + aliases: ['nuclear', 'radioactive'], + }, + { shortname: 'biohazard', emoji: '☣️', aliases: ['danger', 'biohazard'] }, + { shortname: 'mobile_phone_off', emoji: '📴', aliases: ['off', 'phone'] }, + { shortname: 'vibration_mode', emoji: '📳', aliases: ['vibrate', 'phone'] }, + { shortname: 'u6709', emoji: '🈶', aliases: ['japanese', 'available'] }, + { shortname: 'u7121', emoji: '🈚', aliases: ['japanese', 'not'] }, + { shortname: 'u7533', emoji: '🈸', aliases: ['japanese', 'application'] }, + { shortname: 'u55b6', emoji: '🈺', aliases: ['japanese', 'open'] }, + { shortname: 'u6708', emoji: '🈷️', aliases: ['japanese', 'monthly'] }, + { + shortname: 'eight_pointed_black_star', + emoji: '✴️', + aliases: ['star', 'eight'], + }, + { shortname: 'vs', emoji: '🆚', aliases: ['versus', 'vs'] }, + { shortname: 'accept', emoji: '🉑', aliases: ['japanese', 'acceptable'] }, + { shortname: 'white_flower', emoji: '💮', aliases: ['flower', 'white'] }, + { + shortname: 'ideograph_advantage', + emoji: '🉐', + aliases: ['japanese', 'advantage'], + }, + { shortname: 'secret', emoji: '㊙️', aliases: ['japanese', 'secret'] }, + { + shortname: 'congratulations', + emoji: '㊗️', + aliases: ['japanese', 'congratulations'], + }, + { shortname: 'u5408', emoji: '🈴', aliases: ['japanese', 'congratulation'] }, + { shortname: 'u6e80', emoji: '🈵', aliases: ['japanese', 'full'] }, + { shortname: 'u7981', emoji: '🈲', aliases: ['japanese', 'prohibited'] }, + { shortname: 'a', emoji: '🅰️', aliases: ['blood', 'type'] }, + { shortname: 'b', emoji: '🅱️', aliases: ['blood', 'type'] }, + { shortname: 'ab', emoji: '🆎', aliases: ['blood', 'type'] }, + { shortname: 'cl', emoji: '🆑', aliases: ['clear', 'clear'] }, + { shortname: 'o2', emoji: '🅾️', aliases: ['blood', 'type'] }, + { shortname: 'sos', emoji: '🆘', aliases: ['help', 'emergency'] }, + { shortname: 'no_entry', emoji: '⛔', aliases: ['no', 'entry'] }, + { shortname: 'name_badge', emoji: '📛', aliases: ['name', 'badge'] }, + { shortname: 'no_entry_sign', emoji: '🚫', aliases: ['no', 'entry'] }, + { shortname: 'x', emoji: '❌', aliases: ['no', 'cross'] }, + { shortname: 'o', emoji: '⭕', aliases: ['yes', 'circle'] }, + { shortname: 'stop_sign', emoji: '🛑', aliases: ['stop', 'sign'] }, + { shortname: 'anger', emoji: '💢', aliases: ['angry', 'mad'] }, + { shortname: 'hotsprings', emoji: '♨️', aliases: ['hot', 'springs'] }, + { shortname: 'no_pedestrians', emoji: '🚷', aliases: ['no', 'pedestrians'] }, + { shortname: 'do_not_litter', emoji: '🚯', aliases: ['no', 'litter'] }, + { shortname: 'no_bicycles', emoji: '🚳', aliases: ['no', 'bicycles'] }, + { shortname: 'non-potable_water', emoji: '🚱', aliases: ['no', 'water'] }, + { shortname: 'underage', emoji: '🔞', aliases: ['underage', '18'] }, + { shortname: 'no_mobile_phones', emoji: '📵', aliases: ['no', 'phone'] }, + { + shortname: 'exclamation', + emoji: '❗', + aliases: ['exclamation', 'warning'], + }, + { + shortname: 'grey_exclamation', + emoji: '❕', + aliases: ['exclamation', 'grey'], + }, + { shortname: 'question', emoji: '❓', aliases: ['question', 'help'] }, + { shortname: 'grey_question', emoji: '❔', aliases: ['question', 'grey'] }, + { shortname: 'bangbang', emoji: '‼️', aliases: ['exclamation', 'double'] }, + { + shortname: 'interrobang', + emoji: '⁉️', + aliases: ['exclamation', 'question'], + }, + { shortname: '100', emoji: '💯', aliases: ['hundred', 'perfect'] }, + { shortname: 'low_brightness', emoji: '🔅', aliases: ['dim', 'brightness'] }, + { + shortname: 'high_brightness', + emoji: '🔆', + aliases: ['bright', 'brightness'], + }, + { shortname: 'trident', emoji: '🔱', aliases: ['trident', 'fork'] }, + { shortname: 'fleur_de_lis', emoji: '⚜️', aliases: ['fleur', 'lily'] }, + { + shortname: 'part_alternation_mark', + emoji: '〽️', + aliases: ['japanese', 'part'], + }, + { shortname: 'warning', emoji: '⚠️', aliases: ['warning', 'caution'] }, + { + shortname: 'children_crossing', + emoji: '🚸', + aliases: ['children', 'crossing'], + }, + { shortname: 'beginner', emoji: '🔰', aliases: ['beginner', 'leaf'] }, + { shortname: 'recycle', emoji: '♻️', aliases: ['recycle', 'green'] }, + { shortname: 'u6307', emoji: '🈯', aliases: ['japanese', 'point'] }, + { shortname: 'chart', emoji: '💹', aliases: ['chart', 'yen'] }, + { shortname: 'sparkle', emoji: '✨', aliases: ['sparkle', 'shine'] }, + { + shortname: 'eight_spoked_asterisk', + emoji: '✳️', + aliases: ['asterisk', 'eight'], + }, + { + shortname: 'negative_squared_cross_mark', + emoji: '❎', + aliases: ['no', 'cross'], + }, + { shortname: 'white_check_mark', emoji: '✅', aliases: ['yes', 'check'] }, + { + shortname: 'diamond_shape_with_a_dot_inside', + emoji: '💠', + aliases: ['diamond', 'dot'], + }, + { shortname: 'cyclone', emoji: '🌀', aliases: ['cyclone', 'spiral'] }, + { shortname: 'loop', emoji: '➿', aliases: ['loop', 'curly'] }, + { + shortname: 'globe_with_meridians', + emoji: '🌐', + aliases: ['globe', 'world'], + }, + { shortname: 'm', emoji: 'Ⓜ️', aliases: ['metro', 'm'] }, + { shortname: 'atm', emoji: '🏧', aliases: ['atm', 'cash'] }, + { shortname: 'sa', emoji: '🈂️', aliases: ['japanese', 'service'] }, + { + shortname: 'passport_control', + emoji: '🛂', + aliases: ['passport', 'control'], + }, + { shortname: 'customs', emoji: '🛃', aliases: ['customs', 'border'] }, + { shortname: 'baggage_claim', emoji: '🛄', aliases: ['baggage', 'claim'] }, + { shortname: 'left_luggage', emoji: '🛅', aliases: ['left', 'luggage'] }, + { + shortname: 'wheelchair', + emoji: '♿', + aliases: ['wheelchair', 'accessibility'], + }, + { shortname: 'no_smoking', emoji: '🚭', aliases: ['no', 'smoking'] }, + { shortname: 'wc', emoji: '🚾', aliases: ['toilet', 'wc'] }, + { shortname: 'parking', emoji: '🅿️', aliases: ['parking', 'p'] }, + { shortname: 'potable_water', emoji: '🚰', aliases: ['water', 'drinking'] }, + { shortname: 'mens', emoji: '🚹', aliases: ['men', 'male'] }, + { shortname: 'womens', emoji: '🚺', aliases: ['women', 'female'] }, + { shortname: 'baby_symbol', emoji: '🚼', aliases: ['baby', 'symbol'] }, + { shortname: 'restroom', emoji: '🚻', aliases: ['restroom', 'toilet'] }, + { + shortname: 'put_litter_in_its_place', + emoji: '🚮', + aliases: ['litter', 'bin'], + }, + { shortname: 'cinema', emoji: '🎦', aliases: ['cinema', 'movie'] }, + { + shortname: 'signal_strength', + emoji: '📶', + aliases: ['signal', 'strength'], + }, + { shortname: 'koko', emoji: '🈁', aliases: ['japanese', 'here'] }, + { shortname: 'ng', emoji: '🆖', aliases: ['ng', 'no'] }, + { shortname: 'ok', emoji: '🆗', aliases: ['ok', 'okay'] }, + { shortname: 'up', emoji: '🆙', aliases: ['up', 'arrow'] }, + { shortname: 'cool', emoji: '🆒', aliases: ['cool', 'fresh'] }, + { shortname: 'new', emoji: '🆕', aliases: ['new', 'fresh'] }, + { shortname: 'free', emoji: '🆓', aliases: ['free', 'complimentary'] }, + { shortname: 'zero', emoji: '0️⃣', aliases: ['zero', '0'] }, + { shortname: 'one', emoji: '1️⃣', aliases: ['one', '1'] }, + { shortname: 'two', emoji: '2️⃣', aliases: ['two', '2'] }, + { shortname: 'three', emoji: '3️⃣', aliases: ['three', '3'] }, + { shortname: 'four', emoji: '4️⃣', aliases: ['four', '4'] }, + { shortname: 'five', emoji: '5️⃣', aliases: ['five', '5'] }, + { shortname: 'six', emoji: '6️⃣', aliases: ['six', '6'] }, + { shortname: 'seven', emoji: '7️⃣', aliases: ['seven', '7'] }, + { shortname: 'eight', emoji: '8️⃣', aliases: ['eight', '8'] }, + { shortname: 'nine', emoji: '9️⃣', aliases: ['nine', '9'] }, + { shortname: 'keycap_ten', emoji: '🔟', aliases: ['ten', '10'] }, + + // Additional popular emojis + { shortname: 'fire', emoji: '🔥', aliases: ['hot', 'flame'] }, + { shortname: 'boom', emoji: '💥', aliases: ['explosion', 'bang'] }, + { shortname: 'collision', emoji: '💥', aliases: ['explosion', 'bang'] }, + { shortname: 'sweat_drops', emoji: '💦', aliases: ['water', 'drops'] }, + { shortname: 'droplet', emoji: '💧', aliases: ['water', 'drop'] }, + { shortname: 'dash', emoji: '💨', aliases: ['wind', 'fast'] }, + { shortname: 'hole', emoji: '🕳️', aliases: ['hole', 'dark'] }, + { shortname: 'bomb', emoji: '💣', aliases: ['bomb', 'explosive'] }, + { shortname: 'speech_balloon', emoji: '💬', aliases: ['speech', 'bubble'] }, + { shortname: 'eye_speech_bubble', emoji: '👁️‍🗨️', aliases: ['eye', 'speech'] }, + { shortname: 'left_speech_bubble', emoji: '🗨️', aliases: ['left', 'speech'] }, + { shortname: 'right_anger_bubble', emoji: '🗯️', aliases: ['right', 'anger'] }, + { shortname: 'thought_balloon', emoji: '💭', aliases: ['thought', 'bubble'] }, + { shortname: 'zzz', emoji: '💤', aliases: ['sleep', 'tired'] }, + { shortname: 'wave', emoji: '👋', aliases: ['hello', 'goodbye'] }, + { shortname: 'raised_back_of_hand', emoji: '🤚', aliases: ['stop', 'hand'] }, + { + shortname: 'raised_hand_with_fingers_splayed', + emoji: '🖐️', + aliases: ['stop', 'hand'], + }, + { shortname: 'hand', emoji: '✋', aliases: ['stop', 'hand'] }, + { shortname: 'spock-hand', emoji: '🖖', aliases: ['spock', 'vulcan'] }, + { shortname: 'the_horns', emoji: '🤘', aliases: ['rock', 'metal'] }, + { shortname: 'call_me_hand', emoji: '🤙', aliases: ['call', 'phone'] }, + { shortname: 'point_left', emoji: '👈', aliases: ['left', 'point'] }, + { shortname: 'point_right', emoji: '👉', aliases: ['right', 'point'] }, + { shortname: 'point_up_2', emoji: '👆', aliases: ['up', 'point'] }, + { shortname: 'middle_finger', emoji: '🖕', aliases: ['middle', 'finger'] }, + { shortname: 'point_down', emoji: '👇', aliases: ['down', 'point'] }, + { shortname: 'point_up', emoji: '☝️', aliases: ['up', 'point'] }, + { shortname: 'point_up_2', emoji: '👆', aliases: ['up', 'point'] }, + { shortname: 'crossed_fingers', emoji: '🤞', aliases: ['luck', 'cross'] }, + { shortname: 'love_you_gesture', emoji: '🤟', aliases: ['love', 'you'] }, + { shortname: 'metal', emoji: '🤘', aliases: ['rock', 'metal'] }, + { shortname: 'vulcan_salute', emoji: '🖖', aliases: ['spock', 'vulcan'] }, + { + shortname: 'index_pointing_at_the_viewer', + emoji: '🫵', + aliases: ['point', 'you'], + }, + { shortname: 'palms_up_together', emoji: '🤲', aliases: ['prayer', 'hands'] }, + { shortname: 'back_of_hand', emoji: '🤚', aliases: ['back', 'hand'] }, + { + shortname: 'hand_with_index_finger_and_thumb_crossed', + emoji: '🤌', + aliases: ['pinch', 'money'], + }, + { shortname: 'index_pointing_up', emoji: '☝️', aliases: ['up', 'point'] }, + { + shortname: 'index_pointing_at_the_viewer_dark_skin_tone', + emoji: '🫵🏿', + aliases: ['point', 'you', 'dark'], + }, + { + shortname: 'index_pointing_at_the_viewer_light_skin_tone', + emoji: '🫵🏻', + aliases: ['point', 'you', 'light'], + }, + { + shortname: 'index_pointing_at_the_viewer_medium_dark_skin_tone', + emoji: '🫵🏾', + aliases: ['point', 'you', 'medium_dark'], + }, + { + shortname: 'index_pointing_at_the_viewer_medium_light_skin_tone', + emoji: '🫵🏼', + aliases: ['point', 'you', 'medium_light'], + }, + { + shortname: 'index_pointing_at_the_viewer_medium_skin_tone', + emoji: '🫵🏽', + aliases: ['point', 'you', 'medium'], + }, + { shortname: 'thumbs_up', emoji: '👍', aliases: ['yes', 'good', 'approve'] }, + { + shortname: 'thumbs_down', + emoji: '👎', + aliases: ['no', 'bad', 'disapprove'], + }, + { shortname: 'raised_fist', emoji: '✊', aliases: ['fist', 'punch'] }, + { shortname: 'oncoming_fist', emoji: '👊', aliases: ['fist', 'punch'] }, + { shortname: 'left_facing_fist', emoji: '🤛', aliases: ['fist', 'left'] }, + { shortname: 'right_facing_fist', emoji: '🤜', aliases: ['fist', 'right'] }, + { shortname: 'clap', emoji: '👏', aliases: ['applause', 'bravo'] }, + { shortname: 'raised_hands', emoji: '🙌', aliases: ['celebration', 'hands'] }, + { shortname: 'open_hands', emoji: '👐', aliases: ['open', 'hands'] }, + { shortname: 'palms_up_together', emoji: '🤲', aliases: ['prayer', 'hands'] }, + { shortname: 'handshake', emoji: '🤝', aliases: ['deal', 'agreement'] }, + { shortname: 'pray', emoji: '🙏', aliases: ['please', 'thanks', 'hope'] }, + { shortname: 'writing_hand', emoji: '✍️', aliases: ['write', 'signature'] }, + { shortname: 'nail_care', emoji: '💅', aliases: ['nail', 'polish'] }, + { shortname: 'selfie', emoji: '🤳', aliases: ['camera', 'photo'] }, + { shortname: 'muscle', emoji: '💪', aliases: ['strong', 'flex', 'biceps'] }, + { shortname: 'mechanical_arm', emoji: '🦾', aliases: ['arm', 'robot'] }, + { shortname: 'mechanical_leg', emoji: '🦿', aliases: ['leg', 'robot'] }, + { shortname: 'leg', emoji: '🦵', aliases: ['kick', 'limb'] }, + { shortname: 'foot', emoji: '🦶', aliases: ['kick', 'limb'] }, + { shortname: 'ear', emoji: '👂', aliases: ['listen', 'hear'] }, + { + shortname: 'ear_with_hearing_aid', + emoji: '🦻', + aliases: ['hearing', 'aid'], + }, + { shortname: 'nose', emoji: '👃', aliases: ['smell', 'sniff'] }, + { shortname: 'brain', emoji: '🧠', aliases: ['think', 'smart'] }, + { shortname: 'anatomical_heart', emoji: '🫀', aliases: ['heart', 'health'] }, + { shortname: 'lungs', emoji: '🫁', aliases: ['breath', 'health'] }, + { shortname: 'tooth', emoji: '🦷', aliases: ['dental', 'health'] }, + { shortname: 'bone', emoji: '🦴', aliases: ['skeleton', 'health'] }, + { shortname: 'eyes', emoji: '👀', aliases: ['see', 'look', 'watch'] }, + { shortname: 'eye', emoji: '👁️', aliases: ['see', 'look'] }, + { shortname: 'tongue', emoji: '👅', aliases: ['taste', 'lick'] }, + { shortname: 'lips', emoji: '👄', aliases: ['kiss', 'mouth'] }, +]; + +export default emojiList; diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 0bdebf322b..7d387ba2c5 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -26,12 +26,14 @@ import MembersList from '../Mentions/MembersList'; import { TypingUsers } from '../TypingUsers'; import createPendingMessage from '../../lib/createPendingMessage'; import { CommandsList } from '../CommandList'; +import { EmojiList } from '../EmojiList'; import useSettingsStore from '../../store/settingsStore'; import ChannelState from '../ChannelState/ChannelState'; import QuoteMessage from '../QuoteMessage/QuoteMessage'; import { getChatInputStyles } from './ChatInput.styles'; import useShowCommands from '../../hooks/useShowCommands'; import useSearchMentionUser from '../../hooks/useSearchMentionUser'; +import useSearchEmoji from '../../hooks/useSearchEmoji'; import formatSelection from '../../lib/formatSelection'; import { parseEmoji } from '../../lib/emoji'; @@ -56,6 +58,10 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const [showMembersList, setShowMembersList] = useState(false); const [showCommandList, setShowCommandList] = useState(false); const [filteredCommands, setFilteredCommands] = useState([]); + const [showEmojiList, setShowEmojiList] = useState(false); + const [filteredEmojis, setFilteredEmojis] = useState([]); + const [emojiIndex, setEmojiIndex] = useState(-1); + const [startReadEmoji, setStartReadEmoji] = useState(false); const [isMsgLong, setIsMsgLong] = useState(false); const { @@ -144,6 +150,14 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { setShowMembersList ); + const searchEmoji = useSearchEmoji( + startReadEmoji, + setStartReadEmoji, + setFilteredEmojis, + setEmojiIndex, + setShowEmojiList + ); + useEffect(() => { RCInstance.auth.onAuthChange((user) => { if (user) { @@ -423,12 +437,17 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const onTextChange = (e, val) => { sendTypingStart(); const message = val || e.target.value; - messageRef.current.value = parseEmoji(message); + + // Don't parse emojis if user is currently typing emoji autocomplete + const shouldParseEmoji = !message.match(/:([a-zA-Z0-9_+-]*?)$/); + messageRef.current.value = shouldParseEmoji ? parseEmoji(message) : message; + setDisableButton(!messageRef.current.value.length); if (e !== null) { handleNewLine(e, false); searchMentionUser(message); showCommands(e); + searchEmoji(message); } }; @@ -471,7 +490,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { case e.code === 'Enter': e.preventDefault(); - if (!showCommandList && !showMembersList) { + if (!showCommandList && !showMembersList && !showEmojiList) { sendTypingStop(); sendMessage(); } @@ -594,6 +613,18 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { /> )} + {showEmojiList && ( + + )} + { + const currentMessage = messageRef.current.value; + const emojiMatch = currentMessage.match(/:(.*?)$/); + + if (emojiMatch) { + // Replace the :query with the selected emoji + const beforeQuery = currentMessage.substring( + 0, + currentMessage.lastIndexOf(':') + ); + const insertionText = `${beforeQuery}${selectedEmoji.emoji} `; + + messageRef.current.value = insertionText; + + // Set cursor position after the emoji and space + const cursorPosition = insertionText.length; + messageRef.current.setSelectionRange(cursorPosition, cursorPosition); + messageRef.current.focus(); + + // Clear emoji autocomplete state + setFilteredEmojis([]); + setEmojiIndex(-1); + setStartReadEmoji(false); + setShowEmojiList(false); + } + }, + [ + messageRef, + setFilteredEmojis, + setEmojiIndex, + setShowEmojiList, + setStartReadEmoji, + ] + ); + + const setItemRef = (el, index) => { + itemRefs.current[index] = el; + }; + + useEffect(() => { + const handleKeyPress = (event) => { + switch (event.key) { + case 'Enter': { + const selectedEmoji = filteredEmojis[emojiIndex]; + if (selectedEmoji) { + handleEmojiClick(selectedEmoji); + } + break; + } + case 'Escape': { + // Cancel emoji selection + setFilteredEmojis([]); + setEmojiIndex(-1); + setStartReadEmoji(false); + setShowEmojiList(false); + messageRef.current.focus(); + break; + } + case 'ArrowUp': + event.preventDefault(); + setEmojiIndex( + emojiIndex - 1 < 0 ? filteredEmojis.length - 1 : emojiIndex - 1 + ); + break; + case 'ArrowDown': + event.preventDefault(); + setEmojiIndex( + emojiIndex + 1 >= filteredEmojis.length ? 0 : emojiIndex + 1 + ); + break; + default: + break; + } + }; + + document.addEventListener('keydown', handleKeyPress); + + return () => { + document.removeEventListener('keydown', handleKeyPress); + }; + }, [ + emojiIndex, + filteredEmojis, + handleEmojiClick, + setEmojiIndex, + setFilteredEmojis, + setStartReadEmoji, + setShowEmojiList, + messageRef, + ]); + + useEffect(() => { + if (itemRefs.current[emojiIndex]) { + itemRefs.current[emojiIndex].scrollIntoView({ + block: 'nearest', + }); + } + }, [emojiIndex]); + + if (!filteredEmojis || filteredEmojis.length === 0) { + return null; + } + + return ( + +
    + {filteredEmojis.map((emoji, index) => ( +
  • handleEmojiClick(emoji)} + ref={(el) => setItemRef(el, index)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + handleEmojiClick(emoji); + } + }} + style={{ + backgroundColor: index === emojiIndex && 'rgba(0, 0, 0, 0.1)', + color: index === emojiIndex && theme.colors.foreground, + }} + > + {emoji.emoji} + :{emoji.shortname}: +
  • + ))} +
+
+ ); +} + +EmojiList.propTypes = { + emojiIndex: PropTypes.number, + messageRef: PropTypes.object.isRequired, + filteredEmojis: PropTypes.array, + setFilteredEmojis: PropTypes.func.isRequired, + setEmojiIndex: PropTypes.func.isRequired, + setStartReadEmoji: PropTypes.func.isRequired, + setShowEmojiList: PropTypes.func.isRequired, +}; + +export default EmojiList; diff --git a/packages/react/src/views/EmojiList/EmojiList.styles.js b/packages/react/src/views/EmojiList/EmojiList.styles.js new file mode 100644 index 0000000000..098e0a2bd4 --- /dev/null +++ b/packages/react/src/views/EmojiList/EmojiList.styles.js @@ -0,0 +1,53 @@ +import { css } from '@emotion/react'; + +const getEmojiListStyles = (theme) => { + const styles = { + main: css` + margin: 0.2rem 2rem; + display: block; + max-height: 10rem; + overflow-y: auto; + overflow-x: hidden; + border: 1px solid ${theme.colors.border}; + border-radius: 0.2rem; + background-color: ${theme.colors.background}; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + z-index: 1000; + position: relative; + `, + + listItem: css` + cursor: pointer; + display: flex; + align-items: center; + padding: 0.5rem 0.75rem; + font-size: 14px; + border-bottom: 1px solid ${theme.colors.border}; + transition: background-color 0.2s ease; + + &:hover { + background-color: ${theme.colors.secondary}; + } + + &:last-child { + border-bottom: none; + } + `, + + emoji: css` + font-size: 16px; + margin-right: 0.5rem; + flex-shrink: 0; + `, + + shortname: css` + font-family: monospace; + color: ${theme.colors.mutedForeground}; + font-size: 12px; + `, + }; + + return styles; +}; + +export default getEmojiListStyles; diff --git a/packages/react/src/views/EmojiList/index.js b/packages/react/src/views/EmojiList/index.js new file mode 100644 index 0000000000..794f574143 --- /dev/null +++ b/packages/react/src/views/EmojiList/index.js @@ -0,0 +1 @@ +export { default as EmojiList } from './EmojiList'; From d18e390d5cd6fc34752e2f2e864c4b8951102499 Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:54:35 +0530 Subject: [PATCH 134/153] Updated Deprecated Endpoints (#1035) * Updated Deprecated Endpoints * format checks --- packages/api/src/EmbeddedChatApi.ts | 41 +++-------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index a3549af990..4b64e23d54 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -493,34 +493,7 @@ export default class EmbeddedChatApi { async getRoomInfo() { try { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch( - `${this.host}/api/v1/method.call/rooms%3Aget`, - { - body: JSON.stringify({ - message: JSON.stringify({ - msg: "method", - id: null, - method: "rooms/get", - params: [], - }), - }), - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - method: "POST", - } - ); - - const result = await response.json(); - - if (result.success && result.message) { - const parsedMessage = JSON.parse(result.message); - return parsedMessage; - } - return null; + return await this.channelInfo(); } catch (err) { console.error(err); } @@ -699,22 +672,14 @@ export default class EmbeddedChatApi { try { const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; const response = await fetch( - `${this.host}/api/v1/method.call/getUserRoles`, + `${this.host}/api/v1/roles.getUsersInPublicRoles`, { - body: JSON.stringify({ - message: JSON.stringify({ - msg: "method", - id: null, - method: "getUserRoles", - params: [], - }), - }), headers: { "Content-Type": "application/json", "X-Auth-Token": authToken, "X-User-Id": userId, }, - method: "POST", + method: "GET", } ); From 31c86d7a7c9945513fdf8311568151cff3c0041d Mon Sep 17 00:00:00 2001 From: R Pratheek <111165032+Pratheek555@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:31:29 +0530 Subject: [PATCH 135/153] Pasting image directly from clipboard (#1040) --- packages/react/src/hooks/useDropBox.js | 6 +++ .../react/src/views/ChatInput/ChatInput.js | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/react/src/hooks/useDropBox.js b/packages/react/src/hooks/useDropBox.js index 27002ce7c4..a78b395092 100644 --- a/packages/react/src/hooks/useDropBox.js +++ b/packages/react/src/hooks/useDropBox.js @@ -15,10 +15,16 @@ const useDropBox = () => { setData(e.dataTransfer.files[0]); }; + const handlePaste = (file) => { + toggle(); + setData(file); + }; + return { data, handleDrag, handleDragDrop, + handlePaste, }; }; diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index 7d387ba2c5..f6fb0e111f 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -36,6 +36,7 @@ import useSearchMentionUser from '../../hooks/useSearchMentionUser'; import useSearchEmoji from '../../hooks/useSearchEmoji'; import formatSelection from '../../lib/formatSelection'; import { parseEmoji } from '../../lib/emoji'; +import useDropBox from '../../hooks/useDropBox'; const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { const { styleOverrides, classNames } = useComponentOverrides('ChatInput'); @@ -150,6 +151,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { setShowMembersList ); + const { handlePaste } = useDropBox(); const searchEmoji = useSearchEmoji( startReadEmoji, setStartReadEmoji, @@ -463,6 +465,40 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { } }; + const handlePasting = (event) => { + const { clipboardData } = event; + + if (!clipboardData) { + return; + } + + const items = Array.from(clipboardData.items); + if ( + items.some(({ kind, type }) => kind === 'string' && type === 'text/plain') + ) { + return; + } + + const files = items + .filter( + (item) => item.kind === 'file' && item.type.indexOf('image/') !== -1 + ) + .map((item) => { + const fileItem = item.getAsFile(); + + if (!fileItem) { + return; + } + return fileItem; + }) + .filter((file) => !!file); + + if (files.length) { + event.preventDefault(); + handlePaste(files[0]); + } + }; + const onKeyDown = (e) => { switch (true) { case e.ctrlKey && e.code === 'KeyI': { @@ -666,6 +702,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { }} onFocus={handleFocus} onKeyDown={onKeyDown} + onPaste={handlePasting} ref={messageRef} /> From 240152039e8287b2d22ed997f33a06ca63b53575 Mon Sep 17 00:00:00 2001 From: Deepak Bhagat <149673145+deepak0x@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:51:46 +0530 Subject: [PATCH 136/153] fix(react): add missing unique key props to ChatInputFormattingToolbar components (#1102) --- .../react/src/views/ChatInput/ChatInputFormattingToolbar.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index 5d8c20a600..a930009255 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -116,6 +116,7 @@ const ChatInputFormattingToolbar = ({ audio: ( formatter.find((item) => item.name === name)) .map((item) => isPopoverOpen && popOverItems.includes('formatter') ? ( - <> + {item.name} - + ) : ( Date: Sat, 21 Mar 2026 16:50:37 +0530 Subject: [PATCH 137/153] feat: keyboard shortcut info inside formatter's tooltip (#1103) --- packages/react/src/lib/textFormat.js | 14 +++++--------- .../views/ChatInput/ChatInputFormattingToolbar.js | 4 ++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/react/src/lib/textFormat.js b/packages/react/src/lib/textFormat.js index a1978c0033..4a435cf158 100644 --- a/packages/react/src/lib/textFormat.js +++ b/packages/react/src/lib/textFormat.js @@ -1,11 +1,7 @@ export const formatter = [ - { name: 'bold', pattern: '*{{text}}*', tooltip: 'Bold' }, - { name: 'italic', pattern: '_{{text}}_', tooltip: 'Italic' }, - { name: 'strike', pattern: '~{{text}}~', tooltip: 'Strikethrough' }, - { name: 'code', pattern: '`{{text}}`', tooltip: 'Inline code' }, - { - name: 'multiline', - pattern: '```\n{{text}}\n```', - tooltip: 'Multi-line code', - }, + { name: 'bold', pattern: '*{{text}}*', tooltip: 'Bold', shortcut: 'Ctrl + B' }, + { name: 'italic', pattern: '_{{text}}_', tooltip: 'Italic', shortcut: 'Ctrl + I' }, + { name: 'strike', pattern: '~{{text}}~', tooltip: 'Strikethrough', shortcut: '' }, + { name: 'code', pattern: '`{{text}}`', tooltip: 'Inline code', shortcut: '' }, + { name: 'multiline', pattern: '```\n{{text}}\n```', tooltip: 'Multi-line code', shortcut: ''} ]; diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index a930009255..9574bc27fa 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -216,7 +216,7 @@ const ChatInputFormattingToolbar = ({ ) : ( @@ -296,7 +296,7 @@ const ChatInputFormattingToolbar = ({ if (itemInFormatter) { return ( From 97fc430bde1a52ef0b37a8059efc0f19d3987ab1 Mon Sep 17 00:00:00 2001 From: Vivek Yadav <2428045@kiit.ac.in> Date: Sat, 21 Mar 2026 16:53:55 +0530 Subject: [PATCH 138/153] fix: add user-friendly error notification for login failures (#1107) --- packages/react/src/hooks/useRCAuth.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index 83b013353b..37c727a072 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -63,7 +63,11 @@ export const useRCAuth = () => { } } } catch (e) { - console.error('A error occurred while setting up user', e); + console.error('An error occurred while setting up user', e); + dispatchToastMessage({ + type: 'error', + message: 'Unable to connect to server. Please check your connection and try again.', + }); } }; From 2f988c4f0570dcf552159ee418c298857fab7453 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sat, 21 Mar 2026 17:08:29 +0530 Subject: [PATCH 139/153] fix format check Signed-off-by: Zishan Ahmad --- packages/react/src/hooks/useRCAuth.js | 3 +- packages/react/src/lib/textFormat.js | 28 ++++++++++++++++--- .../ChatInput/ChatInputFormattingToolbar.js | 4 ++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js index 37c727a072..0402fb63d9 100644 --- a/packages/react/src/hooks/useRCAuth.js +++ b/packages/react/src/hooks/useRCAuth.js @@ -66,7 +66,8 @@ export const useRCAuth = () => { console.error('An error occurred while setting up user', e); dispatchToastMessage({ type: 'error', - message: 'Unable to connect to server. Please check your connection and try again.', + message: + 'Unable to connect to server. Please check your connection and try again.', }); } }; diff --git a/packages/react/src/lib/textFormat.js b/packages/react/src/lib/textFormat.js index 4a435cf158..7bdff292b0 100644 --- a/packages/react/src/lib/textFormat.js +++ b/packages/react/src/lib/textFormat.js @@ -1,7 +1,27 @@ export const formatter = [ - { name: 'bold', pattern: '*{{text}}*', tooltip: 'Bold', shortcut: 'Ctrl + B' }, - { name: 'italic', pattern: '_{{text}}_', tooltip: 'Italic', shortcut: 'Ctrl + I' }, - { name: 'strike', pattern: '~{{text}}~', tooltip: 'Strikethrough', shortcut: '' }, + { + name: 'bold', + pattern: '*{{text}}*', + tooltip: 'Bold', + shortcut: 'Ctrl + B', + }, + { + name: 'italic', + pattern: '_{{text}}_', + tooltip: 'Italic', + shortcut: 'Ctrl + I', + }, + { + name: 'strike', + pattern: '~{{text}}~', + tooltip: 'Strikethrough', + shortcut: '', + }, { name: 'code', pattern: '`{{text}}`', tooltip: 'Inline code', shortcut: '' }, - { name: 'multiline', pattern: '```\n{{text}}\n```', tooltip: 'Multi-line code', shortcut: ''} + { + name: 'multiline', + pattern: '```\n{{text}}\n```', + tooltip: 'Multi-line code', + shortcut: '', + }, ]; diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index 9574bc27fa..909359f3c7 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -296,7 +296,9 @@ const ChatInputFormattingToolbar = ({ if (itemInFormatter) { return ( From 189a6bc7dd07102e0c6858c2885ae7445e925699 Mon Sep 17 00:00:00 2001 From: Zishan Ahmad Date: Sat, 21 Mar 2026 17:14:38 +0530 Subject: [PATCH 140/153] fix lint Signed-off-by: Zishan Ahmad --- .../react/src/views/ChatInput/ChatInputFormattingToolbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js index 909359f3c7..a68dc340f6 100644 --- a/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js +++ b/packages/react/src/views/ChatInput/ChatInputFormattingToolbar.js @@ -297,7 +297,7 @@ const ChatInputFormattingToolbar = ({ return ( Date: Sat, 21 Mar 2026 17:33:33 +0530 Subject: [PATCH 141/153] fix: replace deprecated getUserRoles method call with REST API (#1104) --- packages/api/src/EmbeddedChatApi.ts | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 4b64e23d54..88eb4c23c2 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -670,28 +670,14 @@ export default class EmbeddedChatApi { async getUserRoles() { try { - const { userId, authToken } = (await this.auth.getCurrentUser()) || {}; - const response = await fetch( - `${this.host}/api/v1/roles.getUsersInPublicRoles`, - { - headers: { - "Content-Type": "application/json", - "X-Auth-Token": authToken, - "X-User-Id": userId, - }, - method: "GET", - } - ); - - const result = await response.json(); - - if (result.success && result.message) { - const parsedMessage = JSON.parse(result.message); - return parsedMessage; + const response = await this.getUsersInRole("admin"); + if (response && response.success) { + return { result: response.users }; } - return null; + return { result: [] }; } catch (err) { console.error(err); + return { result: [] }; } } From f38073c2436ac1226d2bb3bc8e561b0a9afbed67 Mon Sep 17 00:00:00 2001 From: Aryan Verma <121835781+Aryan-Verma-999@users.noreply.github.com> Date: Sun, 17 May 2026 00:26:38 +0530 Subject: [PATCH 142/153] Upgrade to React 19, Storybook 8, and Node 22 (#1291) * chore: upgrade to React 19, Storybook 8, Node 22, and fix LoginForm state * bug fixes * login bug fix * fix: improve variable names in LoginForm and relax Node version check * ci: upgrade Node.js from 16/18 to 22 in all workflows * style: fix Prettier formatting in react package * fix(e2e): bind Vite to 127.0.0.1 to fix Playwright webServer timeout in CI * fix: use semver package for Node version check * chore: add semver as root devDependency * chore: update lockfile * fix(docs): pin webpack to 5.96.1 and update Node/React versions in READMEs --- .github/workflows/build-and-lint.yml | 4 +- .github/workflows/build-pr.yml | 4 +- .github/workflows/deploy.yml | 4 +- .github/workflows/playwright.yml | 2 +- .nvmrc | 2 +- README.md | 2 +- package.json | 5 + packages/api/src/EmbeddedChatApi.ts | 17 +- packages/auth/src/Api.ts | 3 +- packages/auth/src/RocketChatAuth.ts | 11 +- packages/docs/README.md | 8 +- packages/docs/package.json | 9 +- packages/docs/yarn.lock | 2345 +++++++-- packages/e2e-react/.gitignore | 1 + packages/e2e-react/package.json | 8 +- packages/e2e-react/playwright.config.ts | 3 + packages/e2e-react/vite.config.ts | 5 +- packages/htmlembed/package.json | 4 +- packages/layout_editor/package.json | 4 +- packages/markups/package.json | 8 +- packages/react/.storybook/main.js | 10 +- packages/react/package.json | 30 +- packages/react/src/hooks/useShowCommands.js | 5 +- .../AttachmentPreview/PreviewType/audio.js | 2 +- .../AttachmentPreview/PreviewType/image.js | 2 +- packages/react/src/views/ChatBody/ChatBody.js | 52 +- .../react/src/views/ChatHeader/ChatHeader.js | 7 +- .../react/src/views/ChatInput/ChatInput.js | 80 +- packages/react/src/views/EmbeddedChat.js | 11 +- .../react/src/views/LoginForm/LoginForm.js | 42 +- .../react/src/views/Message/MessageToolbox.js | 18 +- packages/ui-elements/.storybook/main.js | 10 +- packages/ui-elements/package.json | 26 +- packages/ui-kit/package.json | 8 +- scripts/node-check.js | 6 +- yarn.lock | 4617 ++++++----------- 36 files changed, 3775 insertions(+), 3600 deletions(-) diff --git a/.github/workflows/build-and-lint.yml b/.github/workflows/build-and-lint.yml index 4d85036e01..5f0e3b5ead 100644 --- a/.github/workflows/build-and-lint.yml +++ b/.github/workflows/build-and-lint.yml @@ -17,12 +17,12 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: 16.19.0 + node-version: 22 - name: Set up Yarn uses: actions/setup-node@v4 with: - node-version: 16.19.0 + node-version: 22 cache: "yarn" - name: Cache dependencies diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index b459fd0cc4..98624f2e04 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "16.19.0" + node-version: "22" - name: Install Dependencies run: yarn install @@ -36,7 +36,7 @@ jobs: - name: Setup Node.js for Docs uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22" - name: "Install dependencies for docs" run: yarn install diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4055f20ca6..08e6fddcf3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "16.19.0" + node-version: "22" - name: Install Dependencies run: yarn install @@ -38,7 +38,7 @@ jobs: - name: Setup Node.js for Docs uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22" - name: "Install dependencies for docs" run: yarn install diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3d4b133a9e..3494052ebf 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v4 with: - node-version: 16.19.0 + node-version: 22 - name: Cache dependencies uses: actions/cache@v4 diff --git a/.nvmrc b/.nvmrc index d60d573ec6..5a60199693 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.19.0 \ No newline at end of file +v22.16.0 \ No newline at end of file diff --git a/README.md b/README.md index 455b2fa897..2c68bde9d8 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Ensure that the "Enable CORS" option is turned on in your Rocket.Chat server. Yo #### Prerequisites -- **Node.js**: Version 16.19.0 is required. Use [Node Version Manager (NVM)](https://github.com/nvm-sh/nvm) for easy switching between Node.js versions. +- **Node.js**: Version 22 (LTS) is required. Use [Node Version Manager (NVM)](https://github.com/nvm-sh/nvm) for easy switching between Node.js versions. To install and use the correct Node.js version, execute the following commands with the specific version number: diff --git a/package.json b/package.json index c589243fe2..907cd18431 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,15 @@ "esbuild": "^0.17.19", "husky": "^9.0.11", "lerna": "^6.6.2", + "semver": "^7.6.0", "typescript": "^5.1.3" }, "dependencies": { "dompurify": "^3.1.6", "validator": "^13.15.15" + }, + "resolutions": { + "react": "^19.0.0", + "react-dom": "^19.0.0" } } diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 88eb4c23c2..3c5c9a8b1c 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -20,6 +20,7 @@ export default class EmbeddedChatApi { onUiInteractionCallbacks: ((data: any) => void)[]; typingUsers: string[]; auth: RocketChatAuth; + private _connectPromise: Promise | null = null; constructor( host: string, @@ -186,6 +187,17 @@ export default class EmbeddedChatApi { * TODO: Add logic to call thread message event listeners. To be done after thread implementation */ async connect() { + // Guard against concurrent connect() calls (e.g. React StrictMode double-invoke) + if (this._connectPromise) { + return this._connectPromise; + } + this._connectPromise = this._doConnect().finally(() => { + this._connectPromise = null; + }); + return this._connectPromise; + } + + private async _doConnect() { try { await this.close(); // before connection, all previous subscriptions should be cancelled await this.rcClient.connect({}); @@ -198,7 +210,6 @@ export default class EmbeddedChatApi { } const message = JSON.parse(JSON.stringify(data)); if (message.ts?.$date) { - console.log(message.ts?.$date); message.ts = message.ts.$date; } if (!message.ts) { @@ -683,14 +694,14 @@ export default class EmbeddedChatApi { async sendTypingStatus(username: string, typing: boolean) { try { - this.rcClient.methodCall( + await this.rcClient.methodCall( "stream-notify-room", `${this.rid}/user-activity`, username, typing ? ["user-typing"] : [] ); } catch (err) { - console.error(err); + // DDP typing indicator fails when connection is temporarily down — expected, safe to ignore } } diff --git a/packages/auth/src/Api.ts b/packages/auth/src/Api.ts index 78d6c82c7a..f8967ff486 100644 --- a/packages/auth/src/Api.ts +++ b/packages/auth/src/Api.ts @@ -44,7 +44,8 @@ export class Api { if (!response.ok) { throw new ApiError(response, "Failed Api Request for " + endpoint); } - const jsonData = await response.json(); + const text = await response.text(); + const jsonData = text.length ? JSON.parse(text) : {}; return { data: jsonData }; } diff --git a/packages/auth/src/RocketChatAuth.ts b/packages/auth/src/RocketChatAuth.ts index 0f2c55f196..769ca55778 100644 --- a/packages/auth/src/RocketChatAuth.ts +++ b/packages/auth/src/RocketChatAuth.ts @@ -36,7 +36,9 @@ class RocketChatAuth { async onAuthChange(callback: (user: object | null) => void) { this.authListeners.push(callback); const user = await this.getCurrentUser(); - callback(user); + if (this.authListeners.includes(callback)) { + callback(user); + } } async removeAuthListener(callback: (user: object | null) => void) { @@ -72,6 +74,7 @@ class RocketChatAuth { } ); this.setUser(response.data); + this.notifyAuthListeners(); return this.currentUser; } @@ -92,6 +95,7 @@ class RocketChatAuth { credentials ); this.setUser(response.data); + this.notifyAuthListeners(); return this.currentUser; } @@ -107,6 +111,7 @@ class RocketChatAuth { api: this.api, }); this.setUser(response.data); + this.notifyAuthListeners(); return this.currentUser; } @@ -190,10 +195,10 @@ class RocketChatAuth { try { const token = await this.getToken(); if (token) { - const user = await this.loginWithResumeToken(token); // will notifyAuthListeners on successful login + const user = await this.loginWithResumeToken(token); if (user) { this.lastFetched = new Date(); - await this.getCurrentUser(); // refresh the token if needed + await this.getCurrentUser(); } } } catch (e) { diff --git a/packages/docs/README.md b/packages/docs/README.md index aaf7803b93..a063ab8a28 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -4,14 +4,14 @@ This is the official documentation website of EmbeddedChat. > **Node.js Version Requirement** > -> The `docs/` folder requires **Node.js v18 or higher** to run correctly. -> If you’re using a lower version (e.g., v16.19.0 from other parts of the monorepo), you may encounter errors. +> The `docs/` folder requires **Node.js v22 or higher** to run correctly. +> If you’re using a lower version (e.g., v18), you may encounter errors. > > Use [NVM](https://github.com/nvm-sh/nvm) to install and switch to the correct version: > > ```bash -> nvm install 18 -> nvm use 18 +> nvm install 22 +> nvm use 22 > ``` ### Installation diff --git a/packages/docs/package.json b/packages/docs/package.json index f14a34ce8a..a9f4bb2f0e 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -19,8 +19,8 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.5.1", @@ -38,7 +38,10 @@ "last 5 safari version" ] }, + "resolutions": { + "webpack": "5.96.1" + }, "engines": { - "node": ">=18.0" + "node": ">=22.0" } } diff --git a/packages/docs/yarn.lock b/packages/docs/yarn.lock index e12862dfd2..8950e7b7f2 100644 --- a/packages/docs/yarn.lock +++ b/packages/docs/yarn.lock @@ -5,126 +5,217 @@ __metadata: version: 6 cacheKey: 8 -"@algolia/autocomplete-core@npm:1.9.3": - version: 1.9.3 - resolution: "@algolia/autocomplete-core@npm:1.9.3" +"@algolia/abtesting@npm:1.18.1": + version: 1.18.1 + resolution: "@algolia/abtesting@npm:1.18.1" dependencies: - "@algolia/autocomplete-plugin-algolia-insights": 1.9.3 - "@algolia/autocomplete-shared": 1.9.3 - checksum: ce78048568660184a4fa3c6548f344a7f5ce0ba45d4cfc233f9756b6d4f360afd5ae3a18efefcd27a626d3a0d6cf22d9cba3e21b217afae62b8e9d11bc4960da + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: a74a4b921d33dc58568cbdffed079ae75d3c005b72eb43376a0c326a4e74599703bc1545a7db8b6bb4e5104e60db3b5504fe18d52ce1fe52612077971367dc0e languageName: node linkType: hard -"@algolia/autocomplete-plugin-algolia-insights@npm:1.9.3": - version: 1.9.3 - resolution: "@algolia/autocomplete-plugin-algolia-insights@npm:1.9.3" +"@algolia/autocomplete-core@npm:1.17.9": + version: 1.17.9 + resolution: "@algolia/autocomplete-core@npm:1.17.9" + dependencies: + "@algolia/autocomplete-plugin-algolia-insights": 1.17.9 + "@algolia/autocomplete-shared": 1.17.9 + checksum: dde242b1a2d8485e6c7bc94d00e25d707aa66dcd276ee1dde13213f1620bf6a1d289a61c657e40c707ca726a8aa009ab5e8229f92ae5cf22266de490b0634d20 + languageName: node + linkType: hard + +"@algolia/autocomplete-plugin-algolia-insights@npm:1.17.9": + version: 1.17.9 + resolution: "@algolia/autocomplete-plugin-algolia-insights@npm:1.17.9" dependencies: - "@algolia/autocomplete-shared": 1.9.3 + "@algolia/autocomplete-shared": 1.17.9 peerDependencies: search-insights: ">= 1 < 3" - checksum: 030695bf692021c27f52a3d4931efed23032796e326d4ae7957ae91b51c36a10dc2d885fb043909e853f961c994b8e9ff087f50bb918cfa075370562251a199f + checksum: 32761d44a407d7c5ecfae98bb78b45a1ca85c59f44167ea36057315fb357c49684e9126bb7a67a513a27bda60a9661cecd6215f2daa903288860201b0b18c745 languageName: node linkType: hard -"@algolia/autocomplete-preset-algolia@npm:1.9.3": - version: 1.9.3 - resolution: "@algolia/autocomplete-preset-algolia@npm:1.9.3" +"@algolia/autocomplete-preset-algolia@npm:1.17.9": + version: 1.17.9 + resolution: "@algolia/autocomplete-preset-algolia@npm:1.17.9" dependencies: - "@algolia/autocomplete-shared": 1.9.3 + "@algolia/autocomplete-shared": 1.17.9 peerDependencies: "@algolia/client-search": ">= 4.9.1 < 6" algoliasearch: ">= 4.9.1 < 6" - checksum: 1ab3273d3054b348eed286ad1a54b21807846326485507b872477b827dc688006d4f14233cebd0bf49b2932ec8e29eca6d76e48a3c9e9e963b25153b987549c0 + checksum: 0dac2aae02121d37466b4ce1ca533420b25cd70e218a9e645e6194bd84a6012a0e94c22125437adb89599ecf14e4488882f91da382c6c9a8d9447e929b317522 languageName: node linkType: hard -"@algolia/autocomplete-shared@npm:1.9.3": - version: 1.9.3 - resolution: "@algolia/autocomplete-shared@npm:1.9.3" +"@algolia/autocomplete-shared@npm:1.17.9": + version: 1.17.9 + resolution: "@algolia/autocomplete-shared@npm:1.17.9" peerDependencies: "@algolia/client-search": ">= 4.9.1 < 6" algoliasearch: ">= 4.9.1 < 6" - checksum: 06014c8b08d30c452de079f48c0235d8fa09904bf511da8dc1b7e491819940fd4ff36b9bf65340242b2e157a26799a3b9aea01feee9c5bf67be3c48d7dff43d7 + checksum: f16223f5995db0deb014a066e3587ec2da76e62b861aa21411be92cb255b7023507803283803d8c960b396a2c6b690951337c32fef34f68c59ecfb3822dee577 + languageName: node + linkType: hard + +"@algolia/cache-browser-local-storage@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/cache-browser-local-storage@npm:4.27.0" + dependencies: + "@algolia/cache-common": 4.27.0 + checksum: f495cc5130caea73effcee371164fd6322deda3c2b78c26f688542e6c44e706925c148cf48675173b2e3ab7eafa8889bc5cccae07c300f2fd5e990d10cbc0b68 + languageName: node + linkType: hard + +"@algolia/cache-common@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/cache-common@npm:4.27.0" + checksum: 0e3629d291ccd4b40919826450bc538a16ec0f1aa77a8f104639dee1de9263a1fa6ff982d1afabae7a14ac91807820d716ed4b8019c0d54dce2b7e9fa361887b + languageName: node + linkType: hard + +"@algolia/cache-in-memory@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/cache-in-memory@npm:4.27.0" + dependencies: + "@algolia/cache-common": 4.27.0 + checksum: 055f6220a7ab4db328102d473bb0e4a40afcc1ad8ec215c401361d063865fde2cfab24c947b66080c2214f6168dbc7a289897513556e6bb74856e32c6a22a438 + languageName: node + linkType: hard + +"@algolia/client-abtesting@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-abtesting@npm:5.52.1" + dependencies: + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: a65f92bcf4e4fb8a851b5f49bfa387d3a44c6d3753af539f83041d0cb40b5dead99474ac5ad6c07cb9301de72aacf294ec153157c92ddc9ead7e172a32432cb5 + languageName: node + linkType: hard + +"@algolia/client-account@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/client-account@npm:4.27.0" + dependencies: + "@algolia/client-common": 4.27.0 + "@algolia/client-search": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: c9afa59d111eb9fa17dc4ba235d17190845dbfb640a39e4e54e3d3146fb64072367d0b8d9d40694c143d99883c9c0ca226eea1b42b967c14e230d64d4ec72c34 + languageName: node + linkType: hard + +"@algolia/client-analytics@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/client-analytics@npm:4.27.0" + dependencies: + "@algolia/client-common": 4.27.0 + "@algolia/client-search": 4.27.0 + "@algolia/requester-common": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: 76515fb1ef89480e50060dd723ff16818b9cb94193243f675dd10d1138ef567930a32c595189fe830c7a96aafc85dd3351c607e817b9e8d57f93fabf7e19c962 languageName: node linkType: hard -"@algolia/cache-browser-local-storage@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/cache-browser-local-storage@npm:4.24.0" +"@algolia/client-analytics@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-analytics@npm:5.52.1" dependencies: - "@algolia/cache-common": 4.24.0 - checksum: f7f9bdb1fa37e788a5cb8c835e526caff2fa097f68736accd4c82ade5e5cb7f5bbd361cf8fc8c2a4628d979d81bd90597bdaed77ca72de8423593067b3d15040 + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: d0572e7abc82642442a468918fb699fca4ca4c365093862d0b45b03a8c72511d2387585a353bd5a9618641f6893c3d37c2f0e35b5a6805b263f27bb314d46dd6 languageName: node linkType: hard -"@algolia/cache-common@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/cache-common@npm:4.24.0" - checksum: bc1d0f8731713f7e6f10cd397b7d8f7464f14a2f4e1decc73a48e99ecbc0fe41bd4df1cc3eb0a4ecf286095e3eb3935b2ea40179de98e11676f8e7d78c622df8 +"@algolia/client-common@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/client-common@npm:4.27.0" + dependencies: + "@algolia/requester-common": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: c8da03a715799646c96e97bf9970192431e39ff8ae3d68e66fd704a8d047d5e417da720ad6110ab8038a178e4ed784d544c5d19651a35fcb97d02dd5d4203c64 + languageName: node + linkType: hard + +"@algolia/client-common@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-common@npm:5.52.1" + checksum: f92954c9b7a9cae5129eaadfe07cff84244dea2b578e30736603826dcfb0a1c0cd2ef2c461202307e11096ba6c1be63a05d0951dfb408aa743e947ddd4b7f1ef languageName: node linkType: hard -"@algolia/cache-in-memory@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/cache-in-memory@npm:4.24.0" +"@algolia/client-insights@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-insights@npm:5.52.1" dependencies: - "@algolia/cache-common": 4.24.0 - checksum: 0476f65f4b622b1b38f050a03b9bf02cf6cc77fc69ec785d16e244770eb2c5eea581b089a346d24bdbc3561be78d383f2a8b81179b801b2af72d9795bc48fee2 + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: a764e6884f7e38b0b1f18d83cb296749aa7d9c497defd2d165d99a9ca2ba6683551220a599551161873abcdae1f9dea52e23c0da54422a73da3fc1bef328fb01 languageName: node linkType: hard -"@algolia/client-account@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/client-account@npm:4.24.0" +"@algolia/client-personalization@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/client-personalization@npm:4.27.0" dependencies: - "@algolia/client-common": 4.24.0 - "@algolia/client-search": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 059cf39f3e48b2e77a26435267284d2d15a7a3c4e904feb2b2ad2dd207a3ca2e2b3597847ec9f3b1141749b25fb2e6091e9933f53cb86ab278b5b93836c85aad + "@algolia/client-common": 4.27.0 + "@algolia/requester-common": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: 479c6dc8d275180aab93f92a33d685f306c7e8c7179c3c8ec4c0c5644b91d028aeb8c1c7ccaed75d95580d9786ca617d6eab82efc3928fa8ea3b416d376719d9 languageName: node linkType: hard -"@algolia/client-analytics@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/client-analytics@npm:4.24.0" +"@algolia/client-personalization@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-personalization@npm:5.52.1" dependencies: - "@algolia/client-common": 4.24.0 - "@algolia/client-search": 4.24.0 - "@algolia/requester-common": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 17540315bc7ed2ed962fe343129ffe6dcd535cd37d4893765b5b3306a5a2b0a32260d116e77c13541bbc932480b14e24cc640eeecae338bebe7b57bc2cf9cde5 + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: 9188f721f32f7c8be597f837b47804a6c0fb5c7e2ea073c80eb6f6659791fedb47f45df11eff0731f3358e2993c5eaf8c6dd4da44847ff6d19a2cdaef144d23d languageName: node linkType: hard -"@algolia/client-common@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/client-common@npm:4.24.0" +"@algolia/client-query-suggestions@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-query-suggestions@npm:5.52.1" dependencies: - "@algolia/requester-common": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 19c6615f9e1b0bbda7dd8ecd285c5bdf48d7067223b06e385a6c69a20a6d6500086619fa0f9e63403cf33220d5d7a288360df55452fdf00f5feca8ca9852758a + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: 302e9f11a8d445044e3231b6dbf6ca18c6d5c0a404f808d2cf5a0d04090d35f1fb5bda59a55eb10d6c6bbf112bce379c66419b4bc9aec9cde77663ab22b3a109 languageName: node linkType: hard -"@algolia/client-personalization@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/client-personalization@npm:4.24.0" +"@algolia/client-search@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/client-search@npm:4.27.0" dependencies: - "@algolia/client-common": 4.24.0 - "@algolia/requester-common": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 9c569c6d846f7c9cf3056b83f2c67d9e796b5afa7e7aa55b1e125a2cf5a7342c96d94e7e2005931145698a1d1fc9a56d692f56a5b09fc4a4291bcc83b73addba + "@algolia/client-common": 4.27.0 + "@algolia/requester-common": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: 6bffce55cc46ee339c9d0d5493b8c11049c0c0a68d5a1687ceec149cf42e22abaec29c12348814d0663099312e9dc90316246d57595c2b12777046f37e971d0b languageName: node linkType: hard -"@algolia/client-search@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/client-search@npm:4.24.0" +"@algolia/client-search@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/client-search@npm:5.52.1" dependencies: - "@algolia/client-common": 4.24.0 - "@algolia/requester-common": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 2d19823994e92490885115188d75994fbcc7a407fbe14f52034b191607a51081ed476e367a65c889666f6b337b00d700203204d55666f182809f01fbd29fd1fb + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: 4378e7e00884b118b602a5aba20a200e836f3847b3e9f55977c734827160116f622d7c21dc3c93189aeb2c648e802efcd34f4f5c8d065f5380dca843c2e2c1b0 languageName: node linkType: hard @@ -135,74 +226,137 @@ __metadata: languageName: node linkType: hard -"@algolia/logger-common@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/logger-common@npm:4.24.0" - checksum: 668fb5a2cbb6aaea7648ae522b5d088241589a9da9f8abb53e2daa89ca2d0bc04307291f57c65de7a332e092cc054cc98cc21b12af81620099632ca85c4ef074 +"@algolia/ingestion@npm:1.52.1": + version: 1.52.1 + resolution: "@algolia/ingestion@npm:1.52.1" + dependencies: + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: eb198762f95480f694b32b548ab22367b8096baa45ae8418c92a63b9eb53b9fdf992733c6d2f38237521b116ab7595c4048a3da5b5a7f1e7104260775566e768 + languageName: node + linkType: hard + +"@algolia/logger-common@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/logger-common@npm:4.27.0" + checksum: 1d3c7b445bda72a2696123fcc9bc4e2702908e258ee0596b738e92c169d2524c26b29c7a5eb1c979f1823f5fbb9537113fe3aa56e57c6240c2f51f1ed80f2ec2 + languageName: node + linkType: hard + +"@algolia/logger-console@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/logger-console@npm:4.27.0" + dependencies: + "@algolia/logger-common": 4.27.0 + checksum: ec187e61c07dd528ae059684dcb99bab1e6b530a8c717b5e5e124da1d998b85a8cd376d55540ea490c2aee2b3bdf24b5d4b41e3f72c4974a15b1bcb309de6189 + languageName: node + linkType: hard + +"@algolia/monitoring@npm:1.52.1": + version: 1.52.1 + resolution: "@algolia/monitoring@npm:1.52.1" + dependencies: + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: e51cf342fb80f95c38d1a4556d0ffef3f3cf007edd965077cd1071acebcd5d76ef11218e6c602f3d623ea93857f1b91fb11406df4edb022c01cb5281698df0b1 + languageName: node + linkType: hard + +"@algolia/recommend@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/recommend@npm:4.27.0" + dependencies: + "@algolia/cache-browser-local-storage": 4.27.0 + "@algolia/cache-common": 4.27.0 + "@algolia/cache-in-memory": 4.27.0 + "@algolia/client-common": 4.27.0 + "@algolia/client-search": 4.27.0 + "@algolia/logger-common": 4.27.0 + "@algolia/logger-console": 4.27.0 + "@algolia/requester-browser-xhr": 4.27.0 + "@algolia/requester-common": 4.27.0 + "@algolia/requester-node-http": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: f9e385ec1ac864acb9430710cae4cdb74e4c9627a83bbc1a9f929b33034998157b939df273dc6924f2665da6e3d986a1ab6d605b34b680a88d59cfca056c4369 + languageName: node + linkType: hard + +"@algolia/recommend@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/recommend@npm:5.52.1" + dependencies: + "@algolia/client-common": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: 6f87737895f9e47ddcd2dafcda3086ac0a85cdcd50af6f159319d404cfda65895a86d98ef9e02ef427a25ee4d828932c9ccf756ad1d42531993aa9b428625bcf languageName: node linkType: hard -"@algolia/logger-console@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/logger-console@npm:4.24.0" +"@algolia/requester-browser-xhr@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/requester-browser-xhr@npm:4.27.0" dependencies: - "@algolia/logger-common": 4.24.0 - checksum: 846d94ecac2e914a2aa7d1ace301cca7371b2bc757c737405eca8d29fc1a26e788387862851c90f611c90f43755367ce676802a21fa37a3bf8531b1a16f5183b + "@algolia/requester-common": 4.27.0 + checksum: 8ca2bc006b68cf703cd659f99db04abcf3ff3e89c0633ecab7eb0d26acad030121fcd3f7e2990efb7fa16f389d0109ee4d4aefc4f95d4d57dacd8ff16289eee9 languageName: node linkType: hard -"@algolia/recommend@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/recommend@npm:4.24.0" +"@algolia/requester-browser-xhr@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/requester-browser-xhr@npm:5.52.1" dependencies: - "@algolia/cache-browser-local-storage": 4.24.0 - "@algolia/cache-common": 4.24.0 - "@algolia/cache-in-memory": 4.24.0 - "@algolia/client-common": 4.24.0 - "@algolia/client-search": 4.24.0 - "@algolia/logger-common": 4.24.0 - "@algolia/logger-console": 4.24.0 - "@algolia/requester-browser-xhr": 4.24.0 - "@algolia/requester-common": 4.24.0 - "@algolia/requester-node-http": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 426468452186cbcf0653c3a8c8a4f911def6232dc262f0a310c4583939c6efc5a1c567dbff99b6c99a93f2ba05f9336a60d3fc6c9a74ad2d8d13f4c4fa55d3d8 + "@algolia/client-common": 5.52.1 + checksum: 6446631d19866f781570adaba883e987f538391e66e96ed2fc7449df4b90d557461d5ad733b3c4e96162b7774429bcad0e03643deb0e5cc2f4a90e739073a024 languageName: node linkType: hard -"@algolia/requester-browser-xhr@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/requester-browser-xhr@npm:4.24.0" +"@algolia/requester-common@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/requester-common@npm:4.27.0" + checksum: ed02765be7d7a769c77efe0c418621fbe53c5800d678c7bd7d85c6b9e3eb3fb0009daa9f50d54257eabf39aec9d3da0d8de5e7a51519771cfd51e770be6a328d + languageName: node + linkType: hard + +"@algolia/requester-fetch@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/requester-fetch@npm:5.52.1" dependencies: - "@algolia/requester-common": 4.24.0 - checksum: 7c32d38d6c7a83357f52134f50271f1ee3df63888b28bc53040a3c74ef73458d80efaf44a5943a3769e84737c2ffd0743e1044a3b5e99ce69289f63e22b50f2a + "@algolia/client-common": 5.52.1 + checksum: 625c6d803ba0b2f307da0a12c6a1cef1f3e2ca774be268825a5b00dc37ab33c7533aa153c1ff1c986c9d23788252d48ac8f133cd948c067ecc1c4c61cee6d102 languageName: node linkType: hard -"@algolia/requester-common@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/requester-common@npm:4.24.0" - checksum: 8f4a49ef0fb4aca42fa3703ddf97ff7f6e9c8492928aa66704ca2f54d3785d2338b64917860a01a42dedb1621279558ca7d549c5b1eb5b7f2742f952fb9865e5 +"@algolia/requester-node-http@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/requester-node-http@npm:4.27.0" + dependencies: + "@algolia/requester-common": 4.27.0 + checksum: 4dcce64b4918b9748db8ea97b6b10125269275b800a5cd9d2012aaae5df512ff06d2cba3ca5b69597a91e1bb0cac3d2b276f3858500f4c833ccc1e35871c0cc0 languageName: node linkType: hard -"@algolia/requester-node-http@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/requester-node-http@npm:4.24.0" +"@algolia/requester-node-http@npm:5.52.1": + version: 5.52.1 + resolution: "@algolia/requester-node-http@npm:5.52.1" dependencies: - "@algolia/requester-common": 4.24.0 - checksum: 387ee892bf35f46be269996de88f9ea12841796aa33cb5088ba6460a48733614a33300ee44bca0af22b6fded05c16ec92631fb998e9a7e1e6a30504d8b407c23 + "@algolia/client-common": 5.52.1 + checksum: 436b9bdb8b8d87b83217ecce4560654babc75e10da8a5b2861526a2521ec88b97ee43c3ea7bb43b5f1d66ebe74b6b10dd41f1269305114b12882e8bbb620b4f8 languageName: node linkType: hard -"@algolia/transporter@npm:4.24.0": - version: 4.24.0 - resolution: "@algolia/transporter@npm:4.24.0" +"@algolia/transporter@npm:4.27.0": + version: 4.27.0 + resolution: "@algolia/transporter@npm:4.27.0" dependencies: - "@algolia/cache-common": 4.24.0 - "@algolia/logger-common": 4.24.0 - "@algolia/requester-common": 4.24.0 - checksum: 2c026a777de5dcb6f3cc94a0cf5f4650fbc7067f56eb98a1ae9b5750815179a73eb2b1d8ae75853a99823afd13584b62430d7649c65a456b2623123f355955b1 + "@algolia/cache-common": 4.27.0 + "@algolia/logger-common": 4.27.0 + "@algolia/requester-common": 4.27.0 + checksum: 50217ead18cb5ddbc097a43da2d770787ac8cff8930a3134751739c4666e782611b3bad1401dc912ba71836311ea8d9fdaaf25b50dddde60297c031a67bff7f0 languageName: node linkType: hard @@ -216,7 +370,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.24.7, @babel/code-frame@npm:^7.8.3": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.24.7": version: 7.24.7 resolution: "@babel/code-frame@npm:7.24.7" dependencies: @@ -226,6 +380,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0, @babel/code-frame@npm:^7.8.3": + version: 7.29.0 + resolution: "@babel/code-frame@npm:7.29.0" + dependencies: + "@babel/helper-validator-identifier": ^7.28.5 + js-tokens: ^4.0.0 + picocolors: ^1.1.1 + checksum: 39f5b303757e4d63bbff8133e251094cd4f952b46e3fa9febc7368d907583911d6a1eded6090876dc1feeff5cf6e134fb19b706f8d58d26c5402cd50e5e1aeb2 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.25.2": version: 7.25.2 resolution: "@babel/compat-data@npm:7.25.2" @@ -233,7 +398,14 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.21.3, @babel/core@npm:^7.23.3": +"@babel/compat-data@npm:^7.28.6, @babel/compat-data@npm:^7.29.3": + version: 7.29.3 + resolution: "@babel/compat-data@npm:7.29.3" + checksum: 977192bab334f66bc8150026340a33ed318c1a7ce18a9323f4c1b86f8ed1d8645bfe5600242bf682717c05c46a4ff06225242207c1d599f4296914b9b4e3efb5 + languageName: node + linkType: hard + +"@babel/core@npm:^7.21.3": version: 7.25.2 resolution: "@babel/core@npm:7.25.2" dependencies: @@ -256,7 +428,43 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.25.0": +"@babel/core@npm:^7.23.3": + version: 7.29.0 + resolution: "@babel/core@npm:7.29.0" + dependencies: + "@babel/code-frame": ^7.29.0 + "@babel/generator": ^7.29.0 + "@babel/helper-compilation-targets": ^7.28.6 + "@babel/helper-module-transforms": ^7.28.6 + "@babel/helpers": ^7.28.6 + "@babel/parser": ^7.29.0 + "@babel/template": ^7.28.6 + "@babel/traverse": ^7.29.0 + "@babel/types": ^7.29.0 + "@jridgewell/remapping": ^2.3.5 + convert-source-map: ^2.0.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.3 + semver: ^6.3.1 + checksum: 85e1df6e213382c46dee27bcd07ed9202fa108a85bb74eb37be656308fd949349171ad2aa17cc84cf0720c908dc9ea6309d25e64d2a7fcdaa63721ce0c67c10b + languageName: node + linkType: hard + +"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.29.0": + version: 7.29.1 + resolution: "@babel/generator@npm:7.29.1" + dependencies: + "@babel/parser": ^7.29.0 + "@babel/types": ^7.29.0 + "@jridgewell/gen-mapping": ^0.3.12 + "@jridgewell/trace-mapping": ^0.3.28 + jsesc: ^3.0.2 + checksum: d8e6863b2d04f684e65ad72731049ac7d754d3a3d1a67cdfc20807b109ba3180ed90d7ccef58ce5d38ded2eaeb71983a76c711eecb9b6266118262378f6c7226 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.25.0": version: 7.25.0 resolution: "@babel/generator@npm:7.25.0" dependencies: @@ -277,6 +485,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": + version: 7.27.3 + resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" + dependencies: + "@babel/types": ^7.27.3 + checksum: 63863a5c936ef82b546ca289c9d1b18fabfc24da5c4ee382830b124e2e79b68d626207febc8d4bffc720f50b2ee65691d7d12cc0308679dee2cd6bdc926b7190 + languageName: node + linkType: hard + "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7" @@ -300,6 +517,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-compilation-targets@npm:7.28.6" + dependencies: + "@babel/compat-data": ^7.28.6 + "@babel/helper-validator-option": ^7.27.1 + browserslist: ^4.24.0 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: 8151e36b74eb1c5e414fe945c189436421f7bfa011884de5be3dd7fd77f12f1f733ff7c982581dfa0a49d8af724450243c2409427114b4a6cfeb8333259d001c + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.24.7, @babel/helper-create-class-features-plugin@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helper-create-class-features-plugin@npm:7.25.0" @@ -317,6 +547,23 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.28.6": + version: 7.29.3 + resolution: "@babel/helper-create-class-features-plugin@npm:7.29.3" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + "@babel/helper-member-expression-to-functions": ^7.28.5 + "@babel/helper-optimise-call-expression": ^7.27.1 + "@babel/helper-replace-supers": ^7.28.6 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + "@babel/traverse": ^7.29.0 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ed7e755d83a59679ea9554b733e2f4b41fd1dd68ef4d8d3e5009930d8ff323f7aabdb8577abea0d98e5cc62a7ce5d14aca501e4446ccfe397c6c2fa3d8164f4b + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.24.7, @babel/helper-create-regexp-features-plugin@npm:^7.25.0": version: 7.25.2 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.25.2" @@ -330,6 +577,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-regexp-features-plugin@npm:^7.27.1, @babel/helper-create-regexp-features-plugin@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.28.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + regexpu-core: ^6.3.1 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: de202103e6ff8cd8da0d62eb269fcceb29857f3fa16173f0ff38188fd514e9ad4901aef1d590ff8ba25381644b42eaf70ad9ba91fda59fe7aa6a5e694cdde267 + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.6.2": version: 0.6.2 resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" @@ -345,6 +605,28 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.6.5, @babel/helper-define-polyfill-provider@npm:^0.6.8": + version: 0.6.8 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.8" + dependencies: + "@babel/helper-compilation-targets": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + debug: ^4.4.3 + lodash.debounce: ^4.0.8 + resolve: ^1.22.11 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 39fef64ade79253836320c7826895d948ab5e8e21479cf29f5d6bb5284126693ca537b6ace9d9b7b515a8be66bd4a8a7d7687f9b25b7574a52dae7790fcd3a4e + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/helper-globals@npm:7.28.0" + checksum: d8d7b91c12dad1ee747968af0cb73baf91053b2bcf78634da2c2c4991fb45ede9bd0c8f9b5f3254881242bc0921218fcb7c28ae885477c25177147e978ce4397 + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-member-expression-to-functions@npm:7.24.8" @@ -355,6 +637,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-member-expression-to-functions@npm:7.28.5" + dependencies: + "@babel/traverse": ^7.28.5 + "@babel/types": ^7.28.5 + checksum: 447d385233bae2eea713df1785f819b5a5ca272950740da123c42d23f491045120f0fbbb5609c091f7a9bbd40f289a442846dde0cb1bf0c59440fa093690cf7c + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-module-imports@npm:7.24.7" @@ -365,6 +657,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-module-imports@npm:7.28.6" + dependencies: + "@babel/traverse": ^7.28.6 + "@babel/types": ^7.28.6 + checksum: 437513aa029898b588a38f7991d7656c539b22f595207d85d0c407240c9e3f2aff8b9d0d7115fdedc91e7fdce4465100549a052024e2fba6a810bcbb7584296b + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.25.0, @babel/helper-module-transforms@npm:^7.25.2": version: 7.25.2 resolution: "@babel/helper-module-transforms@npm:7.25.2" @@ -379,6 +681,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-module-transforms@npm:7.28.6" + dependencies: + "@babel/helper-module-imports": ^7.28.6 + "@babel/helper-validator-identifier": ^7.28.5 + "@babel/traverse": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 522f7d1d08b5e2ccd4ec912aca879bd1506af78d1fb30f46e3e6b4bb69c6ae6ab4e379a879723844230d27dc6d04a55b03f5215cd3141b7a2b40bb4a02f71a9f + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-optimise-call-expression@npm:7.24.7" @@ -388,6 +703,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-optimise-call-expression@npm:7.27.1" + dependencies: + "@babel/types": ^7.27.1 + checksum: 0fb7ee824a384529d6b74f8a58279f9b56bfe3cce332168067dddeab2552d8eeb56dc8eaf86c04a3a09166a316cb92dfc79c4c623cd034ad4c563952c98b464f + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.24.8 resolution: "@babel/helper-plugin-utils@npm:7.24.8" @@ -395,6 +719,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-plugin-utils@npm:7.28.6" + checksum: a0b4caab5e2180b215faa4d141ceac9e82fad9d446b8023eaeb8d82a6e62024726675b07fe8e616dd12f34e2bb59747e8d57aa8adab3e0717d1b8d691b118379 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.24.7, @babel/helper-remap-async-to-generator@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helper-remap-async-to-generator@npm:7.25.0" @@ -408,6 +739,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-remap-async-to-generator@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-remap-async-to-generator@npm:7.27.1" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.1 + "@babel/helper-wrap-function": ^7.27.1 + "@babel/traverse": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 0747397ba013f87dbf575454a76c18210d61c7c9af0f697546b4bcac670b54ddc156330234407b397f0c948738c304c228e0223039bc45eab4fbf46966a5e8cc + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.24.7, @babel/helper-replace-supers@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helper-replace-supers@npm:7.25.0" @@ -421,6 +765,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.27.1, @babel/helper-replace-supers@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/helper-replace-supers@npm:7.28.6" + dependencies: + "@babel/helper-member-expression-to-functions": ^7.28.5 + "@babel/helper-optimise-call-expression": ^7.27.1 + "@babel/traverse": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: aa6530a52010883b6be88465e3b9e789509786a40203650a23a51c315f7442b196e5925fb8e2d66d1e3dc2c604cdc817bd8c5c170dbb322ab5ebc7486fd8a022 + languageName: node + linkType: hard + "@babel/helper-simple-access@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-simple-access@npm:7.24.7" @@ -441,6 +798,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.27.1" + dependencies: + "@babel/traverse": ^7.27.1 + "@babel/types": ^7.27.1 + checksum: 4f380c5d0e0769fa6942a468b0c2d7c8f0c438f941aaa88f785f8752c103631d0904c7b4e76207a3b0e6588b2dec376595370d92ca8f8f1b422c14a69aa146d4 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-string-parser@npm:7.24.8" @@ -448,6 +815,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-string-parser@npm:7.27.1" + checksum: 0a8464adc4b39b138aedcb443b09f4005d86207d7126e5e079177e05c3116107d856ec08282b365e9a79a9872f40f4092a6127f8d74c8a01c1ef789dacfc25d6 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.24.7": version: 7.24.7 resolution: "@babel/helper-validator-identifier@npm:7.24.7" @@ -455,6 +829,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/helper-validator-identifier@npm:7.28.5" + checksum: 5a251a6848e9712aea0338f659a1a3bd334d26219d5511164544ca8ec20774f098c3a6661e9da65a0d085c745c00bb62c8fada38a62f08fa1f8053bc0aeb57e4 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.24.7, @babel/helper-validator-option@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-validator-option@npm:7.24.8" @@ -462,6 +843,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-validator-option@npm:7.27.1" + checksum: db73e6a308092531c629ee5de7f0d04390835b21a263be2644276cb27da2384b64676cab9f22cd8d8dbd854c92b1d7d56fc8517cf0070c35d1c14a8c828b0903 + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helper-wrap-function@npm:7.25.0" @@ -473,6 +861,17 @@ __metadata: languageName: node linkType: hard +"@babel/helper-wrap-function@npm:^7.27.1": + version: 7.28.6 + resolution: "@babel/helper-wrap-function@npm:7.28.6" + dependencies: + "@babel/template": ^7.28.6 + "@babel/traverse": ^7.28.6 + "@babel/types": ^7.28.6 + checksum: 1281f45d55ff291711de7cf05b8132fc28b8d2b30c6c9cf8fce68669bbe318503ed485057d434efa1a4f91ab55d62bf8f3ecb0a889a9f81d357ad4614cd0fa6c + languageName: node + linkType: hard + "@babel/helpers@npm:^7.25.0": version: 7.25.0 resolution: "@babel/helpers@npm:7.25.0" @@ -483,6 +882,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.28.6": + version: 7.29.2 + resolution: "@babel/helpers@npm:7.29.2" + dependencies: + "@babel/template": ^7.28.6 + "@babel/types": ^7.29.0 + checksum: 2c8ce711a639ef334539d3bd48977f57493f71af99e13d3f685fe47b3bc32aa83dbc1380688e19d5df924d958f8f29072f3dcff8110257ba6399524907287189 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.24.7": version: 7.24.7 resolution: "@babel/highlight@npm:7.24.7" @@ -506,6 +915,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": + version: 7.29.3 + resolution: "@babel/parser@npm:7.29.3" + dependencies: + "@babel/types": ^7.29.0 + bin: + parser: ./bin/babel-parser.js + checksum: 046f46996bf4053b6e29f8a7f420f9e0a2878593c1c9a9914a36faca23fc544a307c78a0101ba3ae98936ade68bdde686a83e1ab2b74c2ebb80dc4a9df48476d + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.3": version: 7.25.3 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.3" @@ -518,6 +938,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.28.5" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/traverse": ^7.28.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 749b40a963d5633f554cad0336245cb6c1c1393c70a3fddcf302d86a1a42b35efdd2ed62056b88db66f3900887ae1cee9a3eeec89799c22e0cf65059f0dfd142 + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.25.0" @@ -529,6 +961,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: eb7f4146dc01f1198ce559a90b077e58b951a07521ec414e3c7d4593bf6c4ab5c2af22242a7e9fec085e20299e0ba6ea97f44a45e84ab148141bf9eb959ad25e + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.25.0" @@ -540,6 +983,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 621cfddfcc99a81e74f8b6f9101fd260b27500cb1a568e3ceae9cc8afe9aee45ac3bca3900a2b66c612b1a2366d29ef67d4df5a1c975be727eaad6906f98c2c6 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@npm:^7.29.3": + version: 7.29.3 + resolution: "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@npm:7.29.3" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: fd13198afc9b72c6a4e4868f1592fc8010f390e7601148a71d2d6111664c0242d6d5ff27d8eb77ca4c35ef47f8416daf5dbc8d46a498ac706d69c6b3a0988cd7 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7" @@ -553,6 +1019,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + "@babel/plugin-transform-optional-chaining": ^7.27.1 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: f07aa80272bd7a46b7ba11a4644da6c9b6a5a64e848dfaffdad6f02663adefd512e1aaebe664c4dd95f7ed4f80c872c7f8db8d8e34b47aae0930b412a28711a0 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.25.0" @@ -565,6 +1044,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/traverse": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: f1341f829f809c8685d839669953a478f8a40d1d53f4f5e1972bf39ff4e1ece148319340292d6e0c3641157268b435cbb99b3ac2f3cefe9fca9e81b8f62d6d71 + languageName: node + linkType: hard + "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": version: 7.21.0-placeholder-for-preset-env.2 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" @@ -640,6 +1131,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 25017235e1e2c4ed892aa327a3fa10f4209cc618c6dd7806fc40c07d8d7d24a39743d3d5568b8d1c8f416cffe03c174e78874ded513c9338b07a7ab1dcbab050 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-attributes@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7" @@ -651,6 +1153,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-attributes@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6c8c6a5988dbb9799d6027360d1a5ba64faabf551f2ef11ba4eade0c62253b5c85d44ddc8eb643c74b9acb2bcaa664a950bd5de9a5d4aef291c4f2a48223bb4b + languageName: node + linkType: hard + "@babel/plugin-syntax-import-meta@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" @@ -684,6 +1197,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.27.1, @babel/plugin-syntax-jsx@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-syntax-jsx@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 572e38f5c1bb4b8124300e7e3dd13e82ae84a21f90d3f0786c98cd05e63c78ca1f32d1cfe462dfbaf5e7d5102fa7cd8fd741dfe4f3afc2e01a3b2877dcc8c866 + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -783,6 +1307,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-syntax-typescript@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5c55f9c63bd36cf3d7e8db892294c8f85000f9c1526c3a1cc310d47d1e174f5c6f6605e5cc902c4636d885faba7a9f3d5e5edc6b35e4f3b1fd4c2d58d0304fa5 + languageName: node + linkType: hard + "@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" @@ -806,6 +1341,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-arrow-functions@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 62c2cc0ae2093336b1aa1376741c5ed245c0987d9e4b4c5313da4a38155509a7098b5acce582b6781cc0699381420010da2e3086353344abe0a6a0ec38961eb7 + languageName: node + linkType: hard + "@babel/plugin-transform-async-generator-functions@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-transform-async-generator-functions@npm:7.25.0" @@ -820,6 +1366,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-generator-functions@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.29.0" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-remap-async-to-generator": ^7.27.1 + "@babel/traverse": ^7.29.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bd549b54283034dd3e2f6c4b41b99a0caba0ddc8e9418490a611136ddb01e62235f14b233fcc172902fd1d18eec6e029245d22212566ea5cb5e24c7450d6005d + languageName: node + linkType: hard + "@babel/plugin-transform-async-to-generator@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7" @@ -833,6 +1392,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.28.6" + dependencies: + "@babel/helper-module-imports": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-remap-async-to-generator": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bca5774263ec01dd2bf71c74bbaf7baa183bf03576636b7826c3346be70c8c8cb15cff549112f2983c36885131a0afde6c443591278c281f733ee17f455aa9b1 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7" @@ -844,9 +1416,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.25.0" +"@babel/plugin-transform-block-scoped-functions@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7fb4988ca80cf1fc8345310d5edfe38e86b3a72a302675cdd09404d5064fe1d1fe1283ebe658ad2b71445ecef857bfb29a748064306b5f6c628e0084759c2201 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.25.0" dependencies: "@babel/helper-plugin-utils": ^7.24.8 peerDependencies: @@ -855,6 +1438,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoping@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-block-scoping@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cb4f71ac4fc7b32c2e3cc167eb9e7a1a11562127d702e3b5093567750e9a4eb11a29ae5a917f62741bf9d5792bfe3022cbcdcc7bb927ddb6f627b6749a38c118 + languageName: node + linkType: hard + "@babel/plugin-transform-class-properties@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-class-properties@npm:7.24.7" @@ -867,6 +1461,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-properties@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-class-properties@npm:7.28.6" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 200f30d44b36a768fa3a8cf690db9e333996af2ad14d9fa1b4c91a427ed9302907873b219b4ce87517ca1014a810eb2e929a6a66be68473f72b546fc64d04fbc + languageName: node + linkType: hard + "@babel/plugin-transform-class-static-block@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7" @@ -880,6 +1486,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-static-block@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-class-static-block@npm:7.28.6" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 3db326156f73a0c0d1e2ea4d73e082b9ace2f6a9c965db1c2e51f3a186751b8b91bafb184d05e046bf970b50ecfde1f74862dd895f9a5ea0fad328369d74cfc4 + languageName: node + linkType: hard + "@babel/plugin-transform-classes@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-transform-classes@npm:7.25.0" @@ -896,6 +1514,22 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-classes@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-classes@npm:7.28.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + "@babel/helper-compilation-targets": ^7.28.6 + "@babel/helper-globals": ^7.28.0 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-replace-supers": ^7.28.6 + "@babel/traverse": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bddeefbfd1966272e5da6a0844d68369a0f43c286816c8b379dfd576cf835b8bc652089ef337b0334ff3ae6c9652d56d8332b78a7d29176534265c39856e4822 + languageName: node + linkType: hard + "@babel/plugin-transform-computed-properties@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7" @@ -908,6 +1542,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-computed-properties@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-computed-properties@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/template": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fd1fcc55003a2584c7461bf214ae9e9fce370ad09339319e99e29e5e55a8a3bd485d10805b3d69636a738208761b3a5b0dafdd023534396be45a36409082b014 + languageName: node + linkType: hard + "@babel/plugin-transform-destructuring@npm:^7.24.8": version: 7.24.8 resolution: "@babel/plugin-transform-destructuring@npm:7.24.8" @@ -919,6 +1565,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-destructuring@npm:^7.28.5": + version: 7.28.5 + resolution: "@babel/plugin-transform-destructuring@npm:7.28.5" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/traverse": ^7.28.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 74a06e55e715cfda0fdd8be53d2655d64dfdc28dffaede329d42548fd5b1449ad26a4ce43a24c3fd277b96f8b2010c7b3915afa8297911cda740cc5cc3a81f38 + languageName: node + linkType: hard + "@babel/plugin-transform-dotall-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7" @@ -931,6 +1589,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dotall-regex@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.28.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 866ffbbdee77fa955063b37c75593db8dbbe46b1ebb64cc788ea437e3a9aa41cb7b9afcee617c678a32b6705baa0892ec8e5d4b8af3bbb0ab1b254514ccdbd37 + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-keys@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7" @@ -942,6 +1612,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ef2112d658338e3ff0827f39a53c0cfa211f1cbbe60363bca833a5269df389598ec965e7283600b46533c39cdca82307d0d69c0f518290ec5b00bb713044715b + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.25.0" @@ -954,6 +1635,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.29.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 7fa7b773259a578c9e01c80946f75ecc074520064aa7a87a65db06c7df70766e2fa6be78cda55fa9418a14e30b2b9d595484a46db48074d495d9f877a4276065 + languageName: node + linkType: hard + "@babel/plugin-transform-dynamic-import@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7" @@ -966,6 +1659,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dynamic-import@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7a9fbc8d17148b7f11a1d1ca3990d2c2cd44bd08a45dcaf14f20a017721235b9044b20e6168b6940282bb1b48fb78e6afbdfb9dd9d82fde614e15baa7d579932 + languageName: node + linkType: hard + +"@babel/plugin-transform-explicit-resource-management@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-explicit-resource-management@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/plugin-transform-destructuring": ^7.28.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: be65403694d360793b1b626ac0dfa7c120cfe4dd1c95a81a30b6e7426dc317643e60a486d642e318a4d3d9a7193e72fdb36e2ec140c25c773dcb9c3b1e2854ef + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7" @@ -978,6 +1694,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b232152499370435c7cd4bf3321f58e189150e35ca3722ea16533d33434b97294df1342f5499671ec48e62b71c34cdea0ca8cf317ad12594a10f6fc670315e62 + languageName: node + linkType: hard + "@babel/plugin-transform-export-namespace-from@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7" @@ -990,6 +1717,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-export-namespace-from@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85082923eca317094f08f4953d8ea2a6558b3117826c0b740676983902b7236df1f4213ad844cb38c2dae104753dbe8f1cc51f01567835d476d32f5f544a4385 + languageName: node + linkType: hard + "@babel/plugin-transform-for-of@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-for-of@npm:7.24.7" @@ -1002,6 +1740,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-for-of@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-for-of@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c9224e08de5d80b2c834383d4359aa9e519db434291711434dd996a4f86b7b664ad67b45d65459b7ec11fa582e3e11a3c769b8a8ca71594bdd4e2f0503f84126 + languageName: node + linkType: hard + "@babel/plugin-transform-function-name@npm:^7.25.1": version: 7.25.1 resolution: "@babel/plugin-transform-function-name@npm:7.25.1" @@ -1015,6 +1765,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-function-name@npm:7.27.1" + dependencies: + "@babel/helper-compilation-targets": ^7.27.1 + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/traverse": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 26a2a183c3c52a96495967420a64afc5a09f743a230272a131668abf23001e393afa6371e6f8e6c60f4182bea210ed31d1caf866452d91009c1daac345a52f23 + languageName: node + linkType: hard + "@babel/plugin-transform-json-strings@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-json-strings@npm:7.24.7" @@ -1027,6 +1790,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-json-strings@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-json-strings@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 69d82a1a0a72ed6e6f7969e09cf330516599d79b2b4e680e9dd3c57616a8c6af049b5103456e370ab56642815e80e46ed88bb81e9e059304a85c5fe0bf137c29 + languageName: node + linkType: hard + "@babel/plugin-transform-literals@npm:^7.25.2": version: 7.25.2 resolution: "@babel/plugin-transform-literals@npm:7.25.2" @@ -1038,6 +1812,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-literals@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0a76d12ab19f32dd139964aea7da48cecdb7de0b75e207e576f0f700121fe92367d788f328bf4fb44b8261a0f605c97b44e62ae61cddbb67b14e94c88b411f95 + languageName: node + linkType: hard + "@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7" @@ -1050,6 +1835,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-logical-assignment-operators@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 36095d5d1cfc680e95298b5389a16016da800ae3379b130dabf557e94652c47b06610407e9fa44aaa03e9b0a5aa7b4b93348123985d44a45e369bf5f3497d149 + languageName: node + linkType: hard + "@babel/plugin-transform-member-expression-literals@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7" @@ -1061,6 +1857,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-member-expression-literals@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 804121430a6dcd431e6ffe99c6d1fbbc44b43478113b79c677629e7f877b4f78a06b69c6bfb2747fd84ee91879fe2eb32e4620b53124603086cf5b727593ebe8 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-amd@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7" @@ -1073,6 +1880,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-amd@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-modules-amd@npm:7.27.1" + dependencies: + "@babel/helper-module-transforms": ^7.27.1 + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8bb36d448e438d5d30f4faf19120e8c18aa87730269e65d805bf6032824d175ed738057cc392c2c8a650028f1ae0f346cad8d6b723f31a037b586e2092a7be18 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-commonjs@npm:^7.24.7, @babel/plugin-transform-modules-commonjs@npm:^7.24.8": version: 7.24.8 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.8" @@ -1086,6 +1905,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.27.1, @babel/plugin-transform-modules-commonjs@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.28.6" + dependencies: + "@babel/helper-module-transforms": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b48cab26fda72894c7002a9c783befbc8a643d827c52bdcc5adf83e418ca93224a15aaf7ed2d1e6284627be55913696cfa2119242686cfa77a473bf79314df26 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.25.0": version: 7.25.0 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.25.0" @@ -1100,6 +1931,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.29.4": + version: 7.29.4 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.29.4" + dependencies: + "@babel/helper-module-transforms": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-validator-identifier": ^7.28.5 + "@babel/traverse": ^7.29.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d9cbb30669077756048af990a08ad1ba149785c336024affa49848dc4ffc5948bfaaf52d90bbec711a1f320e19e2c60182dbeff40d81cc5b9a09a87919abe07d + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7" @@ -1112,6 +1957,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-umd@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-modules-umd@npm:7.27.1" + dependencies: + "@babel/helper-module-transforms": ^7.27.1 + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b007dd89231f2eeccf1c71a85629bcb692573303977a4b1c5f19a835ea6b5142c18ef07849bc6d752b874a11bc0ddf3c67468b77c8ee8310290b688a4f01ef31 + languageName: node + linkType: hard + "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7" @@ -1124,6 +1981,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.29.0" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ed8c27699ca82a6c01cbfd39f3de16b90cfea4f8146a358057f76df290d308a66a8bd2e6734e6a87f68c18576e15d2d70548a84cd474d26fdf256c3f5ae44d8c + languageName: node + linkType: hard + "@babel/plugin-transform-new-target@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-new-target@npm:7.24.7" @@ -1135,6 +2004,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-new-target@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-new-target@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 32c8078d843bda001244509442d68fd3af088d7348ba883f45c262b2c817a27ffc553b0d78e7f7a763271b2ece7fac56151baad7a91fb21f5bb1d2f38e5acad7 + languageName: node + linkType: hard + "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7" @@ -1147,6 +2027,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1cdd3ca48a8fffa13dbb9949748d3dd2183cf24110cd55d702da4549205611fc12978b49886be809ec1929ff6304ac4eecc747a33dca2484f9dc655928ab5a89 + languageName: node + linkType: hard + "@babel/plugin-transform-numeric-separator@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7" @@ -1159,6 +2050,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-numeric-separator@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4b5ca60e481e22f0842761a3badca17376a230b5a7e5482338604eb95836c2d0c9c9bde53bdc5c2de1c6a12ae6c12de7464d098bf74b0943f85905ca358f0b68 + languageName: node + linkType: hard + "@babel/plugin-transform-object-rest-spread@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7" @@ -1173,6 +2075,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-rest-spread@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.28.6" + dependencies: + "@babel/helper-compilation-targets": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/plugin-transform-destructuring": ^7.28.5 + "@babel/plugin-transform-parameters": ^7.27.7 + "@babel/traverse": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ab85b1321f86db91aba22ad9d8e6ab65448c983214998012229f5302468527d27b908ad6b14755991c317e35d2f54ec8459a2a094a755999651fe0ac9bd2e9a6 + languageName: node + linkType: hard + "@babel/plugin-transform-object-super@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-object-super@npm:7.24.7" @@ -1185,6 +2102,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-object-super@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/helper-replace-supers": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 46b819cb9a6cd3cfefe42d07875fee414f18d5e66040366ae856116db560ad4e16f3899a0a7fddd6773e0d1458444f94b208b67c0e3b6977a27ea17a5c13dbf6 + languageName: node + linkType: hard + "@babel/plugin-transform-optional-catch-binding@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7" @@ -1197,6 +2126,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-optional-catch-binding@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ee24a17defec056eb9ef01824d7e4a1f65d531af6b4b79acfd0bcb95ce0b47926e80c61897f36f8c01ce733b069c9acdb1c9ce5ec07a729d0dbf9e8d859fe992 + languageName: node + linkType: hard + "@babel/plugin-transform-optional-chaining@npm:^7.24.7, @babel/plugin-transform-optional-chaining@npm:^7.24.8": version: 7.24.8 resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.8" @@ -1210,6 +2150,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-optional-chaining@npm:^7.27.1, @babel/plugin-transform-optional-chaining@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a40dbe709671a436bb69e14524805e10af81b44c422e4fc5dc905cb91adb92d650c9d266c3c2c0da0d410dea89ce784995d4118b7ab6a7544f4923e61590b386 + languageName: node + linkType: hard + "@babel/plugin-transform-parameters@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-parameters@npm:7.24.7" @@ -1221,6 +2173,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-parameters@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/plugin-transform-parameters@npm:7.27.7" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d51f195e1d6ac5d9fce583e9a70a5bfe403e62386e5eb06db9fbc6533f895a98ff7e7c3dcaa311a8e6fa7a9794466e81cdabcba6af9f59d787fb767bfe7868b4 + languageName: node + linkType: hard + "@babel/plugin-transform-private-methods@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-private-methods@npm:7.24.7" @@ -1233,6 +2196,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-private-methods@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-private-methods@npm:7.28.6" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b80179b28f6a165674d0b0d6c6349b13a01dd282b18f56933423c0a33c23fc0626c8f011f859fc20737d021fe966eb8474a5233e4596401482e9ee7fb00e2aa2 + languageName: node + linkType: hard + "@babel/plugin-transform-private-property-in-object@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7" @@ -1247,6 +2222,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-private-property-in-object@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.28.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + "@babel/helper-create-class-features-plugin": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 32a935e44872e90607851be5bc2cd3365f29c0e0e3853ef3e2b6a7da4d08c647379bf2f2dc4f14a9064d7d72e2cf75da85e55baeeec1ffc25cf6088fe24422f7 + languageName: node + linkType: hard + "@babel/plugin-transform-property-literals@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-property-literals@npm:7.24.7" @@ -1258,6 +2246,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-property-literals@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7caec27d5ed8870895c9faf4f71def72745d69da0d8e77903146a4e135fd7bed5778f5f9cebb36c5fba86338e6194dd67a08c033fc84b4299b7eceab6d9630cb + languageName: node + linkType: hard + "@babel/plugin-transform-react-constant-elements@npm:^7.21.3": version: 7.25.1 resolution: "@babel/plugin-transform-react-constant-elements@npm:7.25.1" @@ -1280,6 +2279,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-display-name@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-react-display-name@npm:7.28.0" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 268b1a9192974439d17949e170b01cac2a2aa003c844e2fe3b8361146f42f66487178cffdfa8ce862aa9e6c814bc37f879a70300cb3f067815d15fa6aad04e6d + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx-development@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.24.7" @@ -1291,6 +2301,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx-development@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.27.1" + dependencies: + "@babel/plugin-transform-react-jsx": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b88865d5b8c018992f2332da939faa15c4d4a864c9435a5937beaff3fe43781432cc42e0a5d5631098e0bd4066fc33f5fa72203b388b074c3545fe7aaa21e474 + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx@npm:^7.24.7": version: 7.25.2 resolution: "@babel/plugin-transform-react-jsx@npm:7.25.2" @@ -1306,6 +2327,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx@npm:^7.27.1": + version: 7.28.6 + resolution: "@babel/plugin-transform-react-jsx@npm:7.28.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + "@babel/helper-module-imports": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/plugin-syntax-jsx": ^7.28.6 + "@babel/types": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e7d093b5ed6c06563e801d44d1212b451445d7600756efd7b8b8e6db4585c27fa8145176dcb3350968c59381af6c566dae9b6dc97ec15d2837493b238904d1c2 + languageName: node + linkType: hard + "@babel/plugin-transform-react-pure-annotations@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.7" @@ -1318,6 +2354,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-pure-annotations@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.27.1" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.1 + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a6f591c5e85a1ab0685d4a25afe591fe8d11dc0b73c677cf9560ff8d540d036a1cce9efcb729fc9092def4d854dc304ffdc063a89a9247900b69c516bf971a4c + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-regenerator@npm:7.24.7" @@ -1330,6 +2378,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-regenerator@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/plugin-transform-regenerator@npm:7.29.0" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f48bc814f11239f2bfe010a6e29d5ac2443e7b1d8004e7c022effa111b743491127acf8644cfef475edb86b91f123829585867bc13762652aabd9b85ed6ce61e + languageName: node + linkType: hard + +"@babel/plugin-transform-regexp-modifiers@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-regexp-modifiers@npm:7.28.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 5aacc570034c085afa0165137bb9a04cd4299b86eb9092933a96dcc1132c8f591d9d534419988f5f762b2f70d43a3c719a6b8fa05fdd3b2b1820d01cf85500da + languageName: node + linkType: hard + "@babel/plugin-transform-reserved-words@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7" @@ -1341,19 +2412,30 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:^7.22.9": - version: 7.24.7 - resolution: "@babel/plugin-transform-runtime@npm:7.24.7" +"@babel/plugin-transform-reserved-words@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-reserved-words@npm:7.27.1" dependencies: - "@babel/helper-module-imports": ^7.24.7 - "@babel/helper-plugin-utils": ^7.24.7 - babel-plugin-polyfill-corejs2: ^0.4.10 - babel-plugin-polyfill-corejs3: ^0.10.1 - babel-plugin-polyfill-regenerator: ^0.6.1 + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: dea0b66742d2863b369c06c053e11e15ba785892ea19cccf7aef3c1bdaa38b6ab082e19984c5ea7810d275d9445c5400fcc385ad71ce707ed9256fadb102af3b + languageName: node + linkType: hard + +"@babel/plugin-transform-runtime@npm:^7.22.9": + version: 7.29.0 + resolution: "@babel/plugin-transform-runtime@npm:7.29.0" + dependencies: + "@babel/helper-module-imports": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + babel-plugin-polyfill-corejs2: ^0.4.14 + babel-plugin-polyfill-corejs3: ^0.13.0 + babel-plugin-polyfill-regenerator: ^0.6.5 semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 98bcbbdc833d5c451189a6325f88820fe92973e119c59ce74bf28681cf4687c8280decb55b6c47f22e98c3973ae3a13521c4f51855a2b8577b230ecb1b4ca5b4 + checksum: 1d3a5951396469372d954538fb188479b86afa8e02ca541da8f123250aaed8df65573b68f67087f4b15a5ccff9abc3a3fdb1d9a07fbb85bfcb807168d7364a37 languageName: node linkType: hard @@ -1368,6 +2450,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-shorthand-properties@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fbba6e2aef0b69681acb68202aa249c0598e470cc0853d7ff5bd0171fd6a7ec31d77cfabcce9df6360fc8349eded7e4a65218c32551bd3fc0caaa1ac899ac6d4 + languageName: node + linkType: hard + "@babel/plugin-transform-spread@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-spread@npm:7.24.7" @@ -1380,6 +2473,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-spread@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-spread@npm:7.28.6" + dependencies: + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e4782578904df68f7d2b3e865f20701c71d6aba0027c4794c1dc08a2f805a12892a078dab483714552398a689ad4ff6786cdf4e088b073452aee7db67e37a09c + languageName: node + linkType: hard + "@babel/plugin-transform-sticky-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7" @@ -1391,6 +2496,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-sticky-regex@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e1414a502efba92c7974681767e365a8cda6c5e9e5f33472a9eaa0ce2e75cea0a9bef881ff8dda37c7810ad902f98d3c00ead92a3ac3b73a79d011df85b5a189 + languageName: node + linkType: hard + "@babel/plugin-transform-template-literals@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-template-literals@npm:7.24.7" @@ -1402,6 +2518,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-template-literals@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-template-literals@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 93aad782503b691faef7c0893372d5243df3219b07f1f22cfc32c104af6a2e7acd6102c128439eab15336d048f1b214ca134b87b0630d8cd568bf447f78b25ce + languageName: node + linkType: hard + "@babel/plugin-transform-typeof-symbol@npm:^7.24.8": version: 7.24.8 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.8" @@ -1413,6 +2540,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ed8048c8de72c60969a64cf2273cc6d9275d8fa8db9bd25a1268273a00fb9cbd79931140311411bda1443aa56cb3961fb911d1795abacde7f0482f1d8fdf0356 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:^7.24.7": version: 7.25.2 resolution: "@babel/plugin-transform-typescript@npm:7.25.2" @@ -1428,6 +2566,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.28.5": + version: 7.28.6 + resolution: "@babel/plugin-transform-typescript@npm:7.28.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.27.3 + "@babel/helper-create-class-features-plugin": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-skip-transparent-expression-wrappers": ^7.27.1 + "@babel/plugin-syntax-typescript": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 029add39a37e4a1960a43c3a109680462f631bc63cc8457ea65add2cce3271c9fd4d6a1782177c65ea5f77731e2f8e2bc65a9aec9cc826346ba540ecd0b97e5a + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7" @@ -1439,6 +2592,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-escapes@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d817154bc10758ddd85b716e0bc1af1a1091e088400289ab6b78a1a4d609907ce3d2f1fd51a6fd0e0c8ecbb5f8e3aab4957e0747776d132d2379e85c3ef0520a + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-property-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7" @@ -1451,6 +2615,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-property-regex@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.28.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d14e8c51aa73f592575c1543400fd67d96df6410d75c9dc10dd640fd7eecb37366a2f2368bbdd7529842532eda4af181c921bda95146c6d373c64ea59c6e9991 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7" @@ -1463,6 +2639,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.27.1" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.27.1 + "@babel/helper-plugin-utils": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a34d89a2b75fb78e66d97c3dc90d4877f7e31f43316b52176f95a5dee20e9bb56ecf158eafc42a001676ddf7b393d9e67650bad6b32f5405780f25fb83cd68e3 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-sets-regex@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.7" @@ -1475,7 +2663,19 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:^7.22.9": +"@babel/plugin-transform-unicode-sets-regex@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.28.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.28.5 + "@babel/helper-plugin-utils": ^7.28.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 423971fe2eef9d18782b1c30f5f42613ee510e5b9c08760c5538a0997b36c34495acce261e0e37a27831f81330359230bd1f33c2e1822de70241002b45b7d68e + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.20.2": version: 7.25.3 resolution: "@babel/preset-env@npm:7.25.3" dependencies: @@ -1568,6 +2768,87 @@ __metadata: languageName: node linkType: hard +"@babel/preset-env@npm:^7.22.9": + version: 7.29.5 + resolution: "@babel/preset-env@npm:7.29.5" + dependencies: + "@babel/compat-data": ^7.29.3 + "@babel/helper-compilation-targets": ^7.28.6 + "@babel/helper-plugin-utils": ^7.28.6 + "@babel/helper-validator-option": ^7.27.1 + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ^7.28.5 + "@babel/plugin-bugfix-safari-class-field-initializer-scope": ^7.27.1 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.27.1 + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": ^7.29.3 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.27.1 + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ^7.28.6 + "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 + "@babel/plugin-syntax-import-assertions": ^7.28.6 + "@babel/plugin-syntax-import-attributes": ^7.28.6 + "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 + "@babel/plugin-transform-arrow-functions": ^7.27.1 + "@babel/plugin-transform-async-generator-functions": ^7.29.0 + "@babel/plugin-transform-async-to-generator": ^7.28.6 + "@babel/plugin-transform-block-scoped-functions": ^7.27.1 + "@babel/plugin-transform-block-scoping": ^7.28.6 + "@babel/plugin-transform-class-properties": ^7.28.6 + "@babel/plugin-transform-class-static-block": ^7.28.6 + "@babel/plugin-transform-classes": ^7.28.6 + "@babel/plugin-transform-computed-properties": ^7.28.6 + "@babel/plugin-transform-destructuring": ^7.28.5 + "@babel/plugin-transform-dotall-regex": ^7.28.6 + "@babel/plugin-transform-duplicate-keys": ^7.27.1 + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ^7.29.0 + "@babel/plugin-transform-dynamic-import": ^7.27.1 + "@babel/plugin-transform-explicit-resource-management": ^7.28.6 + "@babel/plugin-transform-exponentiation-operator": ^7.28.6 + "@babel/plugin-transform-export-namespace-from": ^7.27.1 + "@babel/plugin-transform-for-of": ^7.27.1 + "@babel/plugin-transform-function-name": ^7.27.1 + "@babel/plugin-transform-json-strings": ^7.28.6 + "@babel/plugin-transform-literals": ^7.27.1 + "@babel/plugin-transform-logical-assignment-operators": ^7.28.6 + "@babel/plugin-transform-member-expression-literals": ^7.27.1 + "@babel/plugin-transform-modules-amd": ^7.27.1 + "@babel/plugin-transform-modules-commonjs": ^7.28.6 + "@babel/plugin-transform-modules-systemjs": ^7.29.4 + "@babel/plugin-transform-modules-umd": ^7.27.1 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.29.0 + "@babel/plugin-transform-new-target": ^7.27.1 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.28.6 + "@babel/plugin-transform-numeric-separator": ^7.28.6 + "@babel/plugin-transform-object-rest-spread": ^7.28.6 + "@babel/plugin-transform-object-super": ^7.27.1 + "@babel/plugin-transform-optional-catch-binding": ^7.28.6 + "@babel/plugin-transform-optional-chaining": ^7.28.6 + "@babel/plugin-transform-parameters": ^7.27.7 + "@babel/plugin-transform-private-methods": ^7.28.6 + "@babel/plugin-transform-private-property-in-object": ^7.28.6 + "@babel/plugin-transform-property-literals": ^7.27.1 + "@babel/plugin-transform-regenerator": ^7.29.0 + "@babel/plugin-transform-regexp-modifiers": ^7.28.6 + "@babel/plugin-transform-reserved-words": ^7.27.1 + "@babel/plugin-transform-shorthand-properties": ^7.27.1 + "@babel/plugin-transform-spread": ^7.28.6 + "@babel/plugin-transform-sticky-regex": ^7.27.1 + "@babel/plugin-transform-template-literals": ^7.27.1 + "@babel/plugin-transform-typeof-symbol": ^7.27.1 + "@babel/plugin-transform-unicode-escapes": ^7.27.1 + "@babel/plugin-transform-unicode-property-regex": ^7.28.6 + "@babel/plugin-transform-unicode-regex": ^7.27.1 + "@babel/plugin-transform-unicode-sets-regex": ^7.28.6 + "@babel/preset-modules": 0.1.6-no-external-plugins + babel-plugin-polyfill-corejs2: ^0.4.15 + babel-plugin-polyfill-corejs3: ^0.14.0 + babel-plugin-polyfill-regenerator: ^0.6.6 + core-js-compat: ^3.48.0 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4241d9aa5488dc6df958fe866b747fcd2fd1c1385e95e05900bc6377b1c976cb5bc2057200e8a6c1f76dc99566983e27db927b768df09d7617f61d133f760b1a + languageName: node + linkType: hard + "@babel/preset-modules@npm:0.1.6-no-external-plugins": version: 0.1.6-no-external-plugins resolution: "@babel/preset-modules@npm:0.1.6-no-external-plugins" @@ -1581,7 +2862,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.18.6, @babel/preset-react@npm:^7.22.5": +"@babel/preset-react@npm:^7.18.6": version: 7.24.7 resolution: "@babel/preset-react@npm:7.24.7" dependencies: @@ -1597,7 +2878,23 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.21.0, @babel/preset-typescript@npm:^7.22.5": +"@babel/preset-react@npm:^7.22.5": + version: 7.28.5 + resolution: "@babel/preset-react@npm:7.28.5" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/helper-validator-option": ^7.27.1 + "@babel/plugin-transform-react-display-name": ^7.28.0 + "@babel/plugin-transform-react-jsx": ^7.27.1 + "@babel/plugin-transform-react-jsx-development": ^7.27.1 + "@babel/plugin-transform-react-pure-annotations": ^7.27.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 13bc1fe4dde0a29d00323e46749e5beb457844507cb3afa2fefbd85d283c2d4836f9e4a780be735de58a44c505870476dc2838f1f8faf9d6f056481e65f1a0fb + languageName: node + linkType: hard + +"@babel/preset-typescript@npm:^7.21.0": version: 7.24.7 resolution: "@babel/preset-typescript@npm:7.24.7" dependencies: @@ -1612,6 +2909,21 @@ __metadata: languageName: node linkType: hard +"@babel/preset-typescript@npm:^7.22.5": + version: 7.28.5 + resolution: "@babel/preset-typescript@npm:7.28.5" + dependencies: + "@babel/helper-plugin-utils": ^7.27.1 + "@babel/helper-validator-option": ^7.27.1 + "@babel/plugin-syntax-jsx": ^7.27.1 + "@babel/plugin-transform-modules-commonjs": ^7.27.1 + "@babel/plugin-transform-typescript": ^7.28.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 22f889835d9db1e627846e71ca2f02e2d24e2eb9ebcf9845b3b1d37bd3a53787967bafabbbcb342f06aaf7627399a7102ba6ca18f9a0e17066c865d680d2ceb9 + languageName: node + linkType: hard + "@babel/regjsgen@npm:^0.8.0": version: 0.8.0 resolution: "@babel/regjsgen@npm:0.8.0" @@ -1620,16 +2932,15 @@ __metadata: linkType: hard "@babel/runtime-corejs3@npm:^7.22.6": - version: 7.25.0 - resolution: "@babel/runtime-corejs3@npm:7.25.0" + version: 7.29.2 + resolution: "@babel/runtime-corejs3@npm:7.29.2" dependencies: - core-js-pure: ^3.30.2 - regenerator-runtime: ^0.14.0 - checksum: fb23e5afc7b9077f7cec3f17b58d63154a9f329b6746f8296e7b60ade07b4d7d67a90b23bd7196e7d207e8105dd1b847d1b22a0af5a1c681365004cd63244f63 + core-js-pure: ^3.48.0 + checksum: 950b38dce3814c076ebd97ed6b6dbe7d4dd165d32429208d954134df5e221e060320935e5d2316f6172355070056b87eb24b0c2b099f5aa1a6a81ae549a84a36 languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.8.4": version: 7.25.0 resolution: "@babel/runtime@npm:7.25.0" dependencies: @@ -1638,6 +2949,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.22.6": + version: 7.29.2 + resolution: "@babel/runtime@npm:7.29.2" + checksum: d5548d1165de8995f8afc93a5694b8625409be16cd1f2250ac13e331335858ddac3cb9fd278e6c43956a130101a2203f09417938a1a96f9fb70f02b4b4172e1d + languageName: node + linkType: hard + "@babel/template@npm:^7.24.7, @babel/template@npm:^7.25.0": version: 7.25.0 resolution: "@babel/template@npm:7.25.0" @@ -1649,7 +2967,33 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3": +"@babel/template@npm:^7.28.6": + version: 7.28.6 + resolution: "@babel/template@npm:7.28.6" + dependencies: + "@babel/code-frame": ^7.28.6 + "@babel/parser": ^7.28.6 + "@babel/types": ^7.28.6 + checksum: 8ab6383053e226025d9491a6e795293f2140482d14f60c1244bece6bf53610ed1e251d5e164de66adab765629881c7d9416e1e540c716541d2fd0f8f36a013d7 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.5, @babel/traverse@npm:^7.28.6, @babel/traverse@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/traverse@npm:7.29.0" + dependencies: + "@babel/code-frame": ^7.29.0 + "@babel/generator": ^7.29.0 + "@babel/helper-globals": ^7.28.0 + "@babel/parser": ^7.29.0 + "@babel/template": ^7.28.6 + "@babel/types": ^7.29.0 + debug: ^4.3.1 + checksum: fbb5085aa525b5d4ecd9fe2f5885d88413fff6ad9c0fac244c37f96069b6d3af9ce825750cd16af1d97d26fa3d354b38dbbdb5f31430e0d99ed89660ab65430e + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8, @babel/traverse@npm:^7.25.0, @babel/traverse@npm:^7.25.1, @babel/traverse@npm:^7.25.2, @babel/traverse@npm:^7.25.3": version: 7.25.3 resolution: "@babel/traverse@npm:7.25.3" dependencies: @@ -1675,6 +3019,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.5, @babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/types@npm:7.29.0" + dependencies: + "@babel/helper-string-parser": ^7.27.1 + "@babel/helper-validator-identifier": ^7.28.5 + checksum: 83f190438e94c22b2574aaeef7501830311ef266eaabfb06523409f64e2fe855e522951607085d71cad286719adef14e1ba37b671f334a7cd25b0f8506a01e0b + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -1689,25 +3043,25 @@ __metadata: languageName: node linkType: hard -"@docsearch/css@npm:3.6.1": - version: 3.6.1 - resolution: "@docsearch/css@npm:3.6.1" - checksum: 313ca5a0394485d73b8be73ab2119913b68cf8f446e22a4b1a965123c34aba5a130f4dc5af7ec6880126a220d6040439bb95c11b163c4569f6e2ae18d89b4c3d +"@docsearch/css@npm:3.9.0": + version: 3.9.0 + resolution: "@docsearch/css@npm:3.9.0" + checksum: 8e6f5a995d17881c76b31e5364274b3387917ccbc417ba183009f2655dd507244f7009d27807675f09011efcd8e13d80505e7e17eff1a5d93bcd71324a5fc262 languageName: node linkType: hard "@docsearch/react@npm:^3.5.2": - version: 3.6.1 - resolution: "@docsearch/react@npm:3.6.1" + version: 3.9.0 + resolution: "@docsearch/react@npm:3.9.0" dependencies: - "@algolia/autocomplete-core": 1.9.3 - "@algolia/autocomplete-preset-algolia": 1.9.3 - "@docsearch/css": 3.6.1 - algoliasearch: ^4.19.1 + "@algolia/autocomplete-core": 1.17.9 + "@algolia/autocomplete-preset-algolia": 1.17.9 + "@docsearch/css": 3.9.0 + algoliasearch: ^5.14.2 peerDependencies: - "@types/react": ">= 16.8.0 < 19.0.0" - react: ">= 16.8.0 < 19.0.0" - react-dom: ">= 16.8.0 < 19.0.0" + "@types/react": ">= 16.8.0 < 20.0.0" + react: ">= 16.8.0 < 20.0.0" + react-dom: ">= 16.8.0 < 20.0.0" search-insights: ">= 1 < 3" peerDependenciesMeta: "@types/react": @@ -1718,7 +3072,7 @@ __metadata: optional: true search-insights: optional: true - checksum: e3ad0a77932f99126933316b010a6bed30b9b3547a60269708482eea792856843c80df4b8476afb8a99e3b22a0bb37a6bd684992dccc3d277ad261a0bb27473a + checksum: af6c531af5f4c10fb57d4d29ae47fe297e4201c5130492e2c73c34306348bf87ab05b7eeae2cb83a6c33dbe8da3754b82275b86ae0116df65f34a9e51f9291bc languageName: node linkType: hard @@ -2295,6 +3649,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.12": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": ^1.5.0 + "@jridgewell/trace-mapping": ^0.3.24 + checksum: f2105acefc433337145caa3c84bba286de954f61c0bc46279bbd85a9e6a02871089717fa060413cfb6a9d44189fe8313b2d1cabf3a2eb3284d208fd5f75c54ff + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" @@ -2306,6 +3670,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/remapping@npm:^2.3.5": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.24 + checksum: 4a66a7397c3dc9c6b5c14a0024b1f98c5e1d90a0dbc1e5955b5038f2db339904df2a0ee8a66559fafb4fc23ff33700a2639fd40bbdd2e9e82b58b3bdf83738e3 + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.2 resolution: "@jridgewell/resolve-uri@npm:3.1.2" @@ -2337,6 +3711,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.0": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: c2e36e67971f719a8a3a85ef5a5f580622437cc723c35d03ebd0c9c0b06418700ef006f58af742791f71f6a4fc68fcfaf1f6a74ec2f9a3332860e9373459dae7 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" @@ -2347,6 +3728,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: af8fda2431348ad507fbddf8e25f5d08c79ecc94594061ce402cf41bc5aba1a7b3e59bf0fd70a619b35f33983a3f488ceeba8faf56bff784f98bb5394a8b7d47 + languageName: node + linkType: hard + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.5 resolution: "@leichtgewicht/ip-codec@npm:2.0.5" @@ -2770,7 +4161,7 @@ __metadata: languageName: node linkType: hard -"@types/eslint-scope@npm:^3.7.3": +"@types/eslint-scope@npm:^3.7.7": version: 3.7.7 resolution: "@types/eslint-scope@npm:3.7.7" dependencies: @@ -2799,13 +4190,20 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5": +"@types/estree@npm:*, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard +"@types/estree@npm:^1.0.6": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 752c0afee3ec82b8e24484bf6a27dfa093bbf3de4ef1c20ed0364fb6ad2c0c7971e7504ed9a7aaff103a47e2d945ce7a17f74951743dd944782a0735f53170de + languageName: node + linkType: hard + "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": version: 4.19.5 resolution: "@types/express-serve-static-core@npm:4.19.5" @@ -3330,15 +4728,6 @@ __metadata: languageName: node linkType: hard -"acorn-import-attributes@npm:^1.9.5": - version: 1.9.5 - resolution: "acorn-import-attributes@npm:1.9.5" - peerDependencies: - acorn: ^8 - checksum: 1c0c49b6a244503964ae46ae850baccf306e84caf99bc2010ed6103c69a423987b07b520a6c619f075d215388bd4923eccac995886a54309eda049ab78a4be95 - languageName: node - linkType: hard - "acorn-jsx@npm:^5.0.0": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -3357,7 +4746,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.7.1, acorn@npm:^8.8.2": +"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.8.2": version: 8.12.1 resolution: "acorn@npm:8.12.1" bin: @@ -3366,6 +4755,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.14.0": + version: 8.16.0 + resolution: "acorn@npm:8.16.0" + bin: + acorn: bin/acorn + checksum: bbfa466cd0dbd18b4460a85e9d0fc2f35db999380892403c573261beda91f23836db2aa71fd3ae65e94424ad14ff8e2b7bd37c7a2624278fd89137cd6e448c41 + languageName: node + linkType: hard + "address@npm:^1.0.1, address@npm:^1.1.2": version: 1.2.2 resolution: "address@npm:1.2.2" @@ -3426,7 +4824,19 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.2, ajv@npm:^6.12.5": +"ajv@npm:^6.12.2": + version: 6.15.0 + resolution: "ajv@npm:6.15.0" + dependencies: + fast-deep-equal: ^3.1.1 + fast-json-stable-stringify: ^2.0.0 + json-schema-traverse: ^0.4.1 + uri-js: ^4.2.2 + checksum: a8e0308f1b44c3dfd1143911353be51bf8aedc2f2bcd595061755ad241c8450a10e4b657af8ba764c5ec9ae2162010f21d5e0d43763e20d782a8171da99b967a + languageName: node + linkType: hard + +"ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -3451,36 +4861,58 @@ __metadata: linkType: hard "algoliasearch-helper@npm:^3.13.3": - version: 3.22.3 - resolution: "algoliasearch-helper@npm:3.22.3" + version: 3.29.1 + resolution: "algoliasearch-helper@npm:3.29.1" dependencies: "@algolia/events": ^4.0.1 peerDependencies: algoliasearch: ">= 3.1 < 6" - checksum: 18ee77496e6e3aec6b3c1cab7a6c3e103172e3ac39d8dac076f654a28f66e5a7a3f609f694451705e606510b93c9e2c10f1fe738761ff007044c445318ffdb90 - languageName: node - linkType: hard - -"algoliasearch@npm:^4.18.0, algoliasearch@npm:^4.19.1": - version: 4.24.0 - resolution: "algoliasearch@npm:4.24.0" - dependencies: - "@algolia/cache-browser-local-storage": 4.24.0 - "@algolia/cache-common": 4.24.0 - "@algolia/cache-in-memory": 4.24.0 - "@algolia/client-account": 4.24.0 - "@algolia/client-analytics": 4.24.0 - "@algolia/client-common": 4.24.0 - "@algolia/client-personalization": 4.24.0 - "@algolia/client-search": 4.24.0 - "@algolia/logger-common": 4.24.0 - "@algolia/logger-console": 4.24.0 - "@algolia/recommend": 4.24.0 - "@algolia/requester-browser-xhr": 4.24.0 - "@algolia/requester-common": 4.24.0 - "@algolia/requester-node-http": 4.24.0 - "@algolia/transporter": 4.24.0 - checksum: 13cae6ea7ff05e068906dcb101b940bcf1a4ea41008757554c16a7951cdaa3af3094e547e3e51f9e751f68906b5654506e1dd4a1debb1b9d54cbb227ca83e8db + checksum: ce51481bba2f5c1f7ec0c98f8fa811a257057896ac22bbbd19bacaf5d5b91c7d0698feb3ce6d166feb7d2332a41aa62a055f7482e3150d28f90299d1393a8030 + languageName: node + linkType: hard + +"algoliasearch@npm:^4.18.0": + version: 4.27.0 + resolution: "algoliasearch@npm:4.27.0" + dependencies: + "@algolia/cache-browser-local-storage": 4.27.0 + "@algolia/cache-common": 4.27.0 + "@algolia/cache-in-memory": 4.27.0 + "@algolia/client-account": 4.27.0 + "@algolia/client-analytics": 4.27.0 + "@algolia/client-common": 4.27.0 + "@algolia/client-personalization": 4.27.0 + "@algolia/client-search": 4.27.0 + "@algolia/logger-common": 4.27.0 + "@algolia/logger-console": 4.27.0 + "@algolia/recommend": 4.27.0 + "@algolia/requester-browser-xhr": 4.27.0 + "@algolia/requester-common": 4.27.0 + "@algolia/requester-node-http": 4.27.0 + "@algolia/transporter": 4.27.0 + checksum: ce222e3a1343d0ead630d7a4d3b04f01f60d06b56023591441d890a4a6b0e8ba9a08559831dff3e3e5030b60bdfbdec7db77ce34f8ecc53ee421e42d05a5fd34 + languageName: node + linkType: hard + +"algoliasearch@npm:^5.14.2": + version: 5.52.1 + resolution: "algoliasearch@npm:5.52.1" + dependencies: + "@algolia/abtesting": 1.18.1 + "@algolia/client-abtesting": 5.52.1 + "@algolia/client-analytics": 5.52.1 + "@algolia/client-common": 5.52.1 + "@algolia/client-insights": 5.52.1 + "@algolia/client-personalization": 5.52.1 + "@algolia/client-query-suggestions": 5.52.1 + "@algolia/client-search": 5.52.1 + "@algolia/ingestion": 1.52.1 + "@algolia/monitoring": 1.52.1 + "@algolia/recommend": 5.52.1 + "@algolia/requester-browser-xhr": 5.52.1 + "@algolia/requester-fetch": 5.52.1 + "@algolia/requester-node-http": 5.52.1 + checksum: 1412fc996c88aa948fa491eb6c128682ef27fa1edd2562452bf3d6615f0da5b0213bbae28bde76a3d78360647f6a8eb903a9a48f09739730b07c288b5f2e2761 languageName: node linkType: hard @@ -3604,7 +5036,24 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.14, autoprefixer@npm:^10.4.19": +"autoprefixer@npm:^10.4.14": + version: 10.5.0 + resolution: "autoprefixer@npm:10.5.0" + dependencies: + browserslist: ^4.28.2 + caniuse-lite: ^1.0.30001787 + fraction.js: ^5.3.4 + picocolors: ^1.1.1 + postcss-value-parser: ^4.2.0 + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: e6475eb69ca7544a4abf5174b32e63b8d71aa5eb5618b7468b3c1a389b90596a6fdeab5c3fde7ebdb0b711047e307bd2a19c9f8011aa50ddb8b7eb91e71b7196 + languageName: node + linkType: hard + +"autoprefixer@npm:^10.4.19": version: 10.4.20 resolution: "autoprefixer@npm:10.4.20" dependencies: @@ -3623,15 +5072,15 @@ __metadata: linkType: hard "babel-loader@npm:^9.1.3": - version: 9.1.3 - resolution: "babel-loader@npm:9.1.3" + version: 9.2.1 + resolution: "babel-loader@npm:9.2.1" dependencies: find-cache-dir: ^4.0.0 schema-utils: ^4.0.0 peerDependencies: "@babel/core": ^7.12.0 webpack: ">=5" - checksum: b168dde5b8cf11206513371a79f86bb3faa7c714e6ec9fffd420876b61f3d7f5f4b976431095ef6a14bc4d324505126deb91045fd41e312ba49f4deaa166fe28 + checksum: e1858d7625ad7cc8cabe6bbb8657f957041ffb1308375f359e92aa1654f413bfbb86a281bbf7cd4f7fff374d571c637b117551deac0231d779a198d4e4e78331 languageName: node linkType: hard @@ -3657,7 +5106,20 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.10.1, babel-plugin-polyfill-corejs3@npm:^0.10.4": +"babel-plugin-polyfill-corejs2@npm:^0.4.14, babel-plugin-polyfill-corejs2@npm:^0.4.15": + version: 0.4.17 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.17" + dependencies: + "@babel/compat-data": ^7.28.6 + "@babel/helper-define-polyfill-provider": ^0.6.8 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 945f80f413706831b665322690c655f3782ca6fd8c1fbcccaf449d976ebe6151677fb9331442c72e85eae9a05d5e6633be4e15f75d3e788762d825d31f2964ce + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.10.4": version: 0.10.6 resolution: "babel-plugin-polyfill-corejs3@npm:0.10.6" dependencies: @@ -3669,6 +5131,30 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs3@npm:^0.13.0": + version: 0.13.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.13.0" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.5 + core-js-compat: ^3.43.0 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: cf526031acd97ff2124e7c10e15047e6eeb0620d029c687f1dca99916a8fe6cac0e634b84c913db6cb68b7a024f82492ba8fdcc2a6266e7b05bdac2cba0c2434 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.14.0": + version: 0.14.2 + resolution: "babel-plugin-polyfill-corejs3@npm:0.14.2" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.8 + core-js-compat: ^3.48.0 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 4bcaf4da658aaeb7a6534e6b65a6a45539c5f53bec596fefd0b44eebd249e5db8bbf239a421ceaff5933a0a7eee11e45791e4f4e04886cdf47bb1d4b1a8015aa + languageName: node + linkType: hard + "babel-plugin-polyfill-regenerator@npm:^0.6.1": version: 0.6.2 resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" @@ -3680,6 +5166,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.6.5, babel-plugin-polyfill-regenerator@npm:^0.6.6": + version: 0.6.8 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.8" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.8 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 974464353d6f974e97673385aff616a913c0b76039eab8c5317a2d07c661e080f3dcc213e86f3eae40010172a27ab793cda7a290a8a899716f9a22df9b1d92d2 + languageName: node + linkType: hard + "bail@npm:^2.0.0": version: 2.0.2 resolution: "bail@npm:2.0.2" @@ -3694,6 +5191,15 @@ __metadata: languageName: node linkType: hard +"baseline-browser-mapping@npm:^2.10.12": + version: 2.10.30 + resolution: "baseline-browser-mapping@npm:2.10.30" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 1ad8d9af3cebc4b91cf04b99c326fb0aab697ded642f3830deb96838c6dbb5d35ad2a2cad49064c6136b61bd962d77c23cd05db4f2e65f1fe1cfcca44ace9f4e + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -3812,7 +5318,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1, browserslist@npm:^4.23.3": +"browserslist@npm:^4.0.0, browserslist@npm:^4.23.0, browserslist@npm:^4.23.1, browserslist@npm:^4.23.3": version: 4.23.3 resolution: "browserslist@npm:4.23.3" dependencies: @@ -3826,6 +5332,21 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.18.1, browserslist@npm:^4.24.0, browserslist@npm:^4.28.1, browserslist@npm:^4.28.2": + version: 4.28.2 + resolution: "browserslist@npm:4.28.2" + dependencies: + baseline-browser-mapping: ^2.10.12 + caniuse-lite: ^1.0.30001782 + electron-to-chromium: ^1.5.328 + node-releases: ^2.0.36 + update-browserslist-db: ^1.2.3 + bin: + browserslist: cli.js + checksum: 702cdd3462b5eb6f8a9bb3bf7bdc6d6a4141ced6935bb44edb7f3d40edd66198775f2b4a9178682535391293e04e625ba2b5943546d692f42ea080323cecb25e + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -3952,6 +5473,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001782, caniuse-lite@npm:^1.0.30001787": + version: 1.0.30001792 + resolution: "caniuse-lite@npm:1.0.30001792" + checksum: 25e2ba0feb792fbc99e98b236653dccec6387bf9351510b5401ffcb0c25193e47a2ba37e5ce44f92e0fb023670fe077b938ab8e1bad53bc03e95673e5bd5d439 + languageName: node + linkType: hard + "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -4398,10 +5926,19 @@ __metadata: languageName: node linkType: hard -"core-js-pure@npm:^3.30.2": - version: 3.38.0 - resolution: "core-js-pure@npm:3.38.0" - checksum: 29aac7b56778370523f6a58f713c730975b097fea19838f93705730bd95d2da78b116e561e2cda637dde4cebe0a88baf9a5ce4e391732c39cbc5e55dc95bb38c +"core-js-compat@npm:^3.43.0, core-js-compat@npm:^3.48.0": + version: 3.49.0 + resolution: "core-js-compat@npm:3.49.0" + dependencies: + browserslist: ^4.28.1 + checksum: 21afa75a64b30810f4cc61e90758346e8df6bd20dd8da5afe08fc041b5fb766cf7c41c9cbc63f8fb96bef4e4a2a90eb6f2d7bbd20ac53b8ff23a58bc87e40231 + languageName: node + linkType: hard + +"core-js-pure@npm:^3.48.0": + version: 3.49.0 + resolution: "core-js-pure@npm:3.49.0" + checksum: 6d717ad23665135f0ba5fd18645d3ae6c0647752982037e61e571b7bea3910ad67a9482bbb558461a72015679dd25a40d114249e23c4267a69df494d3d4349ab languageName: node linkType: hard @@ -4715,6 +6252,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.3": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: ^2.1.3 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 4805abd570e601acdca85b6aa3757186084a45cff9b2fa6eee1f3b173caa776b45f478b2a71a572d616d2010cea9211d0ac4a02a610e4c18ac4324bde3760834 + languageName: node + linkType: hard + "decode-named-character-reference@npm:^1.0.0": version: 1.0.2 resolution: "decode-named-character-reference@npm:1.0.2" @@ -4907,8 +6456,8 @@ __metadata: "@mdx-js/react": ^3.0.0 clsx: ^2.0.0 prism-react-renderer: ^2.3.0 - react: ^18.0.0 - react-dom: ^18.0.0 + react: ^19.0.0 + react-dom: ^19.0.0 languageName: unknown linkType: soft @@ -5030,6 +6579,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.5.328": + version: 1.5.358 + resolution: "electron-to-chromium@npm:1.5.358" + checksum: cab5eb95a6b7a7fac04018004ffe01072a51c079d3bf0d7f7fda4fb18f0972ba1e198d7822af9ec0072a19a85668dae01b4d787ab9b2a086e3ef47ff61c9468b + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.5.4": version: 1.5.6 resolution: "electron-to-chromium@npm:1.5.6" @@ -5088,13 +6644,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.17.0": - version: 5.17.1 - resolution: "enhanced-resolve@npm:5.17.1" +"enhanced-resolve@npm:^5.17.1": + version: 5.21.3 + resolution: "enhanced-resolve@npm:5.21.3" dependencies: graceful-fs: ^4.2.4 - tapable: ^2.2.0 - checksum: 4bc38cf1cea96456f97503db7280394177d1bc46f8f87c267297d04f795ac5efa81e48115a2f5b6273c781027b5b6bfc5f62b54df629e4d25fa7001a86624f59 + tapable: ^2.3.3 + checksum: 54e48bde85d57937749765b8e32a17392be6b495a5e576f0c382b7340e095b8cbec224cae65fb8408f73fa68095d71e95fba5f10948072ef6fd26fa4dc5d6aeb languageName: node linkType: hard @@ -5165,6 +6721,13 @@ __metadata: languageName: node linkType: hard +"escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 47b029c83de01b0d17ad99ed766347b974b0d628e848de404018f3abee728e987da0d2d370ad4574aa3d5b5bfc368754fd085d69a30f8e75903486ec4b5b709e + languageName: node + linkType: hard + "escape-goat@npm:^4.0.0": version: 4.0.0 resolution: "escape-goat@npm:4.0.0" @@ -5468,15 +7031,6 @@ __metadata: languageName: node linkType: hard -"fast-url-parser@npm:1.1.3": - version: 1.1.3 - resolution: "fast-url-parser@npm:1.1.3" - dependencies: - punycode: ^1.3.2 - checksum: 5043d0c4a8d775ff58504d56c096563c11b113e4cb8a2668c6f824a1cd4fb3812e2fdf76537eb24a7ce4ae7def6bd9747da630c617cf2a4b6ce0c42514e4f21c - languageName: node - linkType: hard - "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -5683,6 +7237,13 @@ __metadata: languageName: node linkType: hard +"fraction.js@npm:^5.3.4": + version: 5.3.4 + resolution: "fraction.js@npm:5.3.4" + checksum: 6ac88ecfdb5fabe3566ae30f79828d448288efbb852cd43ad83afc961fb6923e1d77bc65fbcba8ccda10894114edd419581a050c73d61e368fdd4c3ff416a65a + languageName: node + linkType: hard + "fresh@npm:0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -6053,6 +7614,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.3": + version: 2.0.3 + resolution: "hasown@npm:2.0.3" + dependencies: + function-bind: ^1.1.2 + checksum: bb06756a13dc4e6d1f45993c86c23f12d167c6c30a7dcc907aec5042300b4eb255615a0e5ed2c65014b93bf8bfcff111d991032c5c01ddefb340aa64b329bd55 + languageName: node + linkType: hard + "hast-util-from-parse5@npm:^8.0.0": version: 8.0.1 resolution: "hast-util-from-parse5@npm:8.0.1" @@ -6290,8 +7860,8 @@ __metadata: linkType: hard "html-webpack-plugin@npm:^5.5.3": - version: 5.6.0 - resolution: "html-webpack-plugin@npm:5.6.0" + version: 5.6.7 + resolution: "html-webpack-plugin@npm:5.6.7" dependencies: "@types/html-minifier-terser": ^6.0.0 html-minifier-terser: ^6.0.2 @@ -6306,7 +7876,7 @@ __metadata: optional: true webpack: optional: true - checksum: 32a6e41da538e798fd0be476637d7611a5e8a98a3508f031996e9eb27804dcdc282cb01f847cf5d066f21b49cfb8e21627fcf977ffd0c9bea81cf80e5a65070d + checksum: 043d17f0988fb173dd1a03159fc2e0c9a4d528f7f439b5318c72ec239e3c2be8267167c23a5a09f034c2c14ae0fccf8ab3055bee91151bb832744da2df29bfb6 languageName: node linkType: hard @@ -6481,13 +8051,13 @@ __metadata: linkType: hard "image-size@npm:^1.0.2": - version: 1.1.1 - resolution: "image-size@npm:1.1.1" + version: 1.2.1 + resolution: "image-size@npm:1.2.1" dependencies: queue: 6.0.2 bin: image-size: bin/image-size.js - checksum: 23b3a515dded89e7f967d52b885b430d6a5a903da954fce703130bfb6069d738d80e6588efd29acfaf5b6933424a56535aa7bf06867e4ebd0250c2ee51f19a4a + checksum: 8601ddd4edc1db16f097f5cf585c23214e29c3b8f4d8a8f8d59b8e3bae2338c8a5073236bfff421d8541091a98a38b802ed049203c745286a69d1aac4e5bc4c7 languageName: node linkType: hard @@ -6498,7 +8068,17 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.1.0, import-fresh@npm:^3.3.0": +"import-fresh@npm:^3.1.0": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: ^1.0.0 + resolve-from: ^4.0.0 + checksum: a06b19461b4879cc654d46f8a6244eb55eb053437afd4cbb6613cad6be203811849ed3e4ea038783092879487299fda24af932b86bdfff67c9055ba3612b8c87 + languageName: node + linkType: hard + +"import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -6681,6 +8261,15 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.16.1": + version: 2.16.2 + resolution: "is-core-module@npm:2.16.2" + dependencies: + hasown: ^2.0.3 + checksum: 9317844b4959f8fb268bfc1b4e24033d60058235c2e7273499c2abfd8e4510e7059b1339bd9109766293747daa3e0b5a89095fb2825a866a4093563fe8fdf16f + languageName: node + linkType: hard + "is-decimal@npm:^2.0.0": version: 2.0.1 resolution: "is-decimal@npm:2.0.1" @@ -7022,6 +8611,15 @@ __metadata: languageName: node linkType: hard +"jsesc@npm:^3.0.2, jsesc@npm:~3.1.0": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 19c94095ea026725540c0d29da33ab03144f6bcf2d4159e4833d534976e99e0c09c38cefa9a575279a51fc36b31166f8d6d05c9fe2645d5f15851d690b41f17f + languageName: node + linkType: hard + "jsesc@npm:~0.5.0": version: 0.5.0 resolution: "jsesc@npm:0.5.0" @@ -7232,7 +8830,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -8198,14 +9796,14 @@ __metadata: linkType: hard "mini-css-extract-plugin@npm:^2.7.6": - version: 2.9.0 - resolution: "mini-css-extract-plugin@npm:2.9.0" + version: 2.10.2 + resolution: "mini-css-extract-plugin@npm:2.10.2" dependencies: schema-utils: ^4.0.0 tapable: ^2.2.1 peerDependencies: webpack: ^5.0.0 - checksum: ae192c67ba85ac8bffeab66774635bf90181f00d5dd6cf95412426192599ddf5506fb4b1550acbd7a5476476e39db53c770dd40f8378f7baf5de96e3fec4e6e9 + checksum: eb64d835d50fce8c181153b580f989661c37c0e56ddff7c1362051643180a31c8f8055d0d8dccc0dd2b022b1010d2957fe3097010f37acd33edfeb29bfc3734e languageName: node linkType: hard @@ -8216,7 +9814,16 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.1.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1": +"minimatch@npm:3.1.5, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: ^1.1.7 + checksum: 47ef6f412c08be045a7291d11b1c40777925accf7252dc6d3caa39b1bfbb3a7ea390ba7aba464d762d783265c644143d2c8a204e6b5763145024d52ee65a1941 + languageName: node + linkType: hard + +"minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -8355,7 +9962,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3": +"ms@npm:2.1.3, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -8374,6 +9981,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.11": + version: 3.3.12 + resolution: "nanoid@npm:3.3.12" + bin: + nanoid: bin/nanoid.cjs + checksum: 38699257447dc59e21e73e0510d0dfb16b7a610d9ca80633d5c3a68f9b4298c990513d30404ca8f163c2d03225ee01695ff8898bea6179183f38f0477b7635ac + languageName: node + linkType: hard + "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -8453,6 +10069,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.36": + version: 2.0.44 + resolution: "node-releases@npm:2.0.44" + checksum: a96278a9ec8f3d67b796693047499fc64724f97cb7fb8aa50e8e5a9408f4f2c1e2eb8f3ee44c43bfd34b0331514d9a8ad5efc7a98e7b720a4b9cd9b4d0f0c2da + languageName: node + linkType: hard + "nopt@npm:^7.0.0": version: 7.2.1 resolution: "nopt@npm:7.2.1" @@ -8866,10 +10489,10 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:2.2.1": - version: 2.2.1 - resolution: "path-to-regexp@npm:2.2.1" - checksum: b921a74e7576e25b06ad1635abf7e8125a29220d2efc2b71d74b9591f24a27e6f09078fa9a1b27516a097ea0637b7cab79d19b83d7f36a8ef3ef5422770e89d9 +"path-to-regexp@npm:3.3.0": + version: 3.3.0 + resolution: "path-to-regexp@npm:3.3.0" + checksum: bb249d08804f7961dd44fb175466c900b893c56e909db8e2a66ec12b9d9a964af269eb7a50892c933f52b47315953dfdb4279639fbce20977c3625a9ef3055fe languageName: node linkType: hard @@ -8907,6 +10530,13 @@ __metadata: languageName: node linkType: hard +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -9365,7 +10995,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.26, postcss@npm:^8.4.33, postcss@npm:^8.4.38": +"postcss@npm:^8.4.21, postcss@npm:^8.4.24, postcss@npm:^8.4.33": version: 8.4.41 resolution: "postcss@npm:8.4.41" dependencies: @@ -9376,6 +11006,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.26, postcss@npm:^8.4.38": + version: 8.5.14 + resolution: "postcss@npm:8.5.14" + dependencies: + nanoid: ^3.3.11 + picocolors: ^1.1.1 + source-map-js: ^1.2.1 + checksum: ec17d1519cd997b43aceb82bfa959f380085591269e286c53d5ba76eb1989525e7cde106a44f1565516fcbb50f206eb1858cc2cd5e5aaea3a8ee793886c8232c + languageName: node + linkType: hard + "pretty-error@npm:^4.0.0": version: 4.0.0 resolution: "pretty-error@npm:4.0.0" @@ -9481,13 +11122,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^1.3.2": - version: 1.4.1 - resolution: "punycode@npm:1.4.1" - checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 - languageName: node - linkType: hard - "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -9617,22 +11251,21 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.0.0": - version: 18.3.1 - resolution: "react-dom@npm:18.3.1" +"react-dom@npm:^19.0.0": + version: 19.2.6 + resolution: "react-dom@npm:19.2.6" dependencies: - loose-envify: ^1.1.0 - scheduler: ^0.23.2 + scheduler: ^0.27.0 peerDependencies: - react: ^18.3.1 - checksum: 298954ecd8f78288dcaece05e88b570014d8f6dce5db6f66e6ee91448debeb59dcd31561dddb354eee47e6c1bb234669459060deb238ed0213497146e555a0b9 + react: ^19.2.6 + checksum: 9ecaff123867286a8fd18fb1028f0b493da4a48faacd38d97d4bde16c756d314869836ae74cd95733c5ebcff8e2cb568d0c06a1b18e2e7482eb898cc5b8dd92f languageName: node linkType: hard "react-error-overlay@npm:^6.0.11": - version: 6.0.11 - resolution: "react-error-overlay@npm:6.0.11" - checksum: ce7b44c38fadba9cedd7c095cf39192e632daeccf1d0747292ed524f17dcb056d16bc197ddee5723f9dd888f0b9b19c3b486c430319e30504289b9296f2d2c42 + version: 6.1.0 + resolution: "react-error-overlay@npm:6.1.0" + checksum: 4f0785ea14390e333d040e7d7d6f8b915ad9bd4b8ae6eb28e1a5338f23a0325798d20deea7572c3c129bd1d32c432b01e7a4d40ca99710e2fa1f8157929e6cda languageName: node linkType: hard @@ -9644,15 +11277,15 @@ __metadata: linkType: hard "react-helmet-async@npm:*": - version: 2.0.5 - resolution: "react-helmet-async@npm:2.0.5" + version: 3.0.0 + resolution: "react-helmet-async@npm:3.0.0" dependencies: invariant: ^2.2.4 react-fast-compare: ^3.2.2 shallowequal: ^1.1.0 peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - checksum: cc2d13496f6fdee6b5f9472d3f7369db3e70e4fc1a55793708c2bbd90d48b0dedc725fd066f987c7a3d74b03a29bd5e65b9f40fa29bd8239e7cfb526aff4d4b6 + react: ^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 78273903cbdbbf4ed555b38ec95e9080d7526e4194845f664c0374146fcdeb6ab4e02aee70b3b852b88272a3992c8c1dae2bf3fed188ccfa2d19b8e5e2432545 languageName: node linkType: hard @@ -9680,11 +11313,11 @@ __metadata: linkType: hard "react-json-view-lite@npm:^1.2.0": - version: 1.4.0 - resolution: "react-json-view-lite@npm:1.4.0" + version: 1.5.0 + resolution: "react-json-view-lite@npm:1.5.0" peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 - checksum: 420921258478da46a54887b6e4740e6cf21c7264eba95c33d6264fdf71c482f0746c1345eb187a4a52b31d2a3a951f88c7af338b9fccbced2a918751dd98c844 + checksum: e298621f6437ee06545bdb9e11265d92a60a6394ca8ea40bf12b50422a62f7a485998bf96d0b8b7cb95658d0122dbc900a814a572ddaaf59053657a27ccc7f33 languageName: node linkType: hard @@ -9759,12 +11392,10 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.0.0": - version: 18.3.1 - resolution: "react@npm:18.3.1" - dependencies: - loose-envify: ^1.1.0 - checksum: a27bcfa8ff7c15a1e50244ad0d0c1cb2ad4375eeffefd266a64889beea6f6b64c4966c9b37d14ee32d6c9fcd5aa6ba183b6988167ab4d127d13e7cb5b386a376 +"react@npm:^19.0.0": + version: 19.2.6 + resolution: "react@npm:19.2.6" + checksum: e250029064bff3f3308ee61e52c44bc9a0c938f6ef11a3f39c31db487ca173eb9b4b0cd3261e32613d3498df0da5586f3c582bc83cef54ad99bc5643772c4535 languageName: node linkType: hard @@ -9837,6 +11468,15 @@ __metadata: languageName: node linkType: hard +"regenerate-unicode-properties@npm:^10.2.2": + version: 10.2.2 + resolution: "regenerate-unicode-properties@npm:10.2.2" + dependencies: + regenerate: ^1.4.2 + checksum: 7ae4c1c32460c4360e3118c45eec0621424908f430fdd6f162c9172067786bf2b1682fbc885a33b26bc85e76e06f4d3f398b52425e801b0bb0cbae147dafb0b2 + languageName: node + linkType: hard + "regenerate@npm:^1.4.2": version: 1.4.2 resolution: "regenerate@npm:1.4.2" @@ -9874,6 +11514,20 @@ __metadata: languageName: node linkType: hard +"regexpu-core@npm:^6.3.1": + version: 6.4.0 + resolution: "regexpu-core@npm:6.4.0" + dependencies: + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.2.2 + regjsgen: ^0.8.0 + regjsparser: ^0.13.0 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.2.1 + checksum: a316eb988599b7fb9d77f4adb937c41c022504dc91ddd18175c11771addc7f1d9dce550f34e36038395e459a2cf9ffc0d663bfe8d3c6c186317ca000ba79a8cf + languageName: node + linkType: hard + "registry-auth-token@npm:^5.0.1": version: 5.0.2 resolution: "registry-auth-token@npm:5.0.2" @@ -9892,6 +11546,24 @@ __metadata: languageName: node linkType: hard +"regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "regjsgen@npm:0.8.0" + checksum: a1d925ff14a4b2be774e45775ee6b33b256f89c42d480e6d85152d2133f18bd3d6af662161b226fa57466f7efec367eaf7ccd2a58c0ec2a1306667ba2ad07b0d + languageName: node + linkType: hard + +"regjsparser@npm:^0.13.0": + version: 0.13.1 + resolution: "regjsparser@npm:0.13.1" + dependencies: + jsesc: ~3.1.0 + bin: + regjsparser: bin/parser + checksum: 7a4e60e1487b6a0702e35540f882c0c6e0151f7f567c6a4c480c5397a3cab05f6d2bf5f64cdbcdf341e41caf232cae801a4db9b531c26eed3ca946b3c50ccb34 + languageName: node + linkType: hard + "regjsparser@npm:^0.9.1": version: 0.9.1 resolution: "regjsparser@npm:0.9.1" @@ -10073,7 +11745,21 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.14.2": +"resolve@npm:^1.1.6, resolve@npm:^1.22.11": + version: 1.22.12 + resolution: "resolve@npm:1.22.12" + dependencies: + es-errors: ^1.3.0 + is-core-module: ^2.16.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 4dc5a614b32142ef9ab455b242ed33c472c4ea50df17dbe1e9dac5fe0eebd7d5fdb7cb9cc8ad2165e5e0f07694498a74e7fbd6cc1599e20d84682cce1b80a4dc + languageName: node + linkType: hard + +"resolve@npm:^1.14.2": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -10086,7 +11772,21 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.22.11#~builtin": + version: 1.22.12 + resolution: "resolve@patch:resolve@npm%3A1.22.12#~builtin::version=1.22.12&hash=c3c19d" + dependencies: + es-errors: ^1.3.0 + is-core-module: ^2.16.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 0cc5b060cbe081c85c331ac2eb08e8a54f0a195b899d5001822e5d3e2b335da651b1eed3d259fea904c22a0da9324a061e0e7ceab5dbeb5bcab5250b625754e1 + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.14.2#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -10198,12 +11898,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.2": - version: 0.23.2 - resolution: "scheduler@npm:0.23.2" - dependencies: - loose-envify: ^1.1.0 - checksum: 3e82d1f419e240ef6219d794ff29c7ee415fbdc19e038f680a10c067108e06284f1847450a210b29bbaf97b9d8a97ced5f624c31c681248ac84c80d56ad5a2c4 +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 92644ead0a9443e20f9d24132fe93675b156209b9eeb35ea245f8a86768d0cc0fcca56f341eeef21d9b6dd8e72d6d5e260eb5a41d34b05cd605dd45a29f572ef languageName: node linkType: hard @@ -10286,7 +11984,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.4": +"semver@npm:^7.3.2": + version: 7.8.0 + resolution: "semver@npm:7.8.0" + bin: + semver: bin/semver.js + checksum: 68e38bc26ed1191d7c78d2b711bdffc2f8b1d05a1caadda41a1d7e1a9d32e1da5ae5b645de5c5f2b27bde830d7e9c1cbeeafcb8fda091830411df7d40be405b1 + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.4": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -10326,18 +12033,17 @@ __metadata: linkType: hard "serve-handler@npm:^6.1.5": - version: 6.1.5 - resolution: "serve-handler@npm:6.1.5" + version: 6.1.7 + resolution: "serve-handler@npm:6.1.7" dependencies: bytes: 3.0.0 content-disposition: 0.5.2 - fast-url-parser: 1.1.3 mime-types: 2.1.18 - minimatch: 3.1.2 + minimatch: 3.1.5 path-is-inside: 1.0.2 - path-to-regexp: 2.2.1 + path-to-regexp: 3.3.0 range-parser: 1.2.0 - checksum: 7a98ca9cbf8692583b6cde4deb3941cff900fa38bf16adbfccccd8430209bab781e21d9a1f61c9c03e226f9f67689893bbce25941368f3ddaf985fc3858b49dc + checksum: eb03d807336f8186257a28cb48fd03b3ddb2dda918896fbe190e8f721354d276c3ff35506f20728aa89f6962f73fb721b3e1e4fe92847a4e758b95980d5c3daa languageName: node linkType: hard @@ -10428,7 +12134,14 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.7.3, shell-quote@npm:^1.8.1": +"shell-quote@npm:^1.7.3": + version: 1.8.3 + resolution: "shell-quote@npm:1.8.3" + checksum: 550dd84e677f8915eb013d43689c80bb114860649ec5298eb978f40b8f3d4bc4ccb072b82c094eb3548dc587144bb3965a8676f0d685c1cf4c40b5dc27166242 + languageName: node + linkType: hard + +"shell-quote@npm:^1.8.1": version: 1.8.1 resolution: "shell-quote@npm:1.8.1" checksum: 5f01201f4ef504d4c6a9d0d283fa17075f6770bfbe4c5850b074974c68062f37929ca61700d95ad2ac8822e14e8c4b990ca0e6e9272e64befd74ce5e19f0736b @@ -10592,6 +12305,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 4eb0cd997cdf228bc253bcaff9340afeb706176e64868ecd20efbe6efea931465f43955612346d6b7318789e5265bdc419bc7669c1cebe3db0eb255f57efa76b + languageName: node + linkType: hard + "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -10695,9 +12415,9 @@ __metadata: linkType: hard "std-env@npm:^3.0.1": - version: 3.7.0 - resolution: "std-env@npm:3.7.0" - checksum: 4f489d13ff2ab838c9acd4ed6b786b51aa52ecacdfeaefe9275fcb220ff2ac80c6e95674723508fd29850a694569563a8caaaea738eb82ca16429b3a0b50e510 + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 51d641b36b0fae494a546fb8446d39a837957fbf902c765c62bd12af8e50682d141c4087ca032f1192fa90330c4f6ff23fd6c9795324efacd1684e814471e0e0 languageName: node linkType: hard @@ -10903,13 +12623,20 @@ __metadata: languageName: node linkType: hard -"tapable@npm:^2.0.0, tapable@npm:^2.1.1, tapable@npm:^2.2.0, tapable@npm:^2.2.1": +"tapable@npm:^2.0.0, tapable@npm:^2.1.1, tapable@npm:^2.2.1": version: 2.2.1 resolution: "tapable@npm:2.2.1" checksum: 3b7a1b4d86fa940aad46d9e73d1e8739335efd4c48322cb37d073eb6f80f5281889bf0320c6d8ffcfa1a0dd5bfdbd0f9d037e252ef972aca595330538aac4d51 languageName: node linkType: hard +"tapable@npm:^2.3.3": + version: 2.3.3 + resolution: "tapable@npm:2.3.3" + checksum: 6f37a59e82a2daedd0fbfc231f6e6004389a9d4bcf8ab8f2d61f96f9f4fd4cbb087799627c5d644d75f518df2abbbc9b9ac699945e0c9a0c610f2a3ca92e0265 + languageName: node + linkType: hard + "tar@npm:^6.1.11, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" @@ -11110,6 +12837,13 @@ __metadata: languageName: node linkType: hard +"unicode-match-property-value-ecmascript@npm:^2.2.1": + version: 2.2.1 + resolution: "unicode-match-property-value-ecmascript@npm:2.2.1" + checksum: e6c73e07bb4dc4aa399797a14b170e84a30ed290bcf97cc4305cf67dde8744119721ce17cef03f4f9d4ff48654bfa26eadc7fe1e8dd4b71b8f3b2e9a9742f013 + languageName: node + linkType: hard + "unicode-property-aliases-ecmascript@npm:^2.0.0": version: 2.1.0 resolution: "unicode-property-aliases-ecmascript@npm:2.1.0" @@ -11254,6 +12988,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.2.3": + version: 1.2.3 + resolution: "update-browserslist-db@npm:1.2.3" + dependencies: + escalade: ^3.2.0 + picocolors: ^1.1.1 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 6f209a97ae8eacdd3a1ef2eb365adf49d1e2a757e5b2dd4ac87dc8c99236cbe3e572d3e605a87dd7b538a11751b71d9f93edc47c7405262a293a493d155316cd + languageName: node + linkType: hard + "update-notifier@npm:^6.0.2": version: 6.0.2 resolution: "update-notifier@npm:6.0.2" @@ -11512,20 +13260,19 @@ __metadata: languageName: node linkType: hard -"webpack@npm:^5.88.1": - version: 5.93.0 - resolution: "webpack@npm:5.93.0" +"webpack@npm:5.96.1": + version: 5.96.1 + resolution: "webpack@npm:5.96.1" dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.5 + "@types/eslint-scope": ^3.7.7 + "@types/estree": ^1.0.6 "@webassemblyjs/ast": ^1.12.1 "@webassemblyjs/wasm-edit": ^1.12.1 "@webassemblyjs/wasm-parser": ^1.12.1 - acorn: ^8.7.1 - acorn-import-attributes: ^1.9.5 - browserslist: ^4.21.10 + acorn: ^8.14.0 + browserslist: ^4.24.0 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.17.0 + enhanced-resolve: ^5.17.1 es-module-lexer: ^1.2.1 eslint-scope: 5.1.1 events: ^3.2.0 @@ -11545,7 +13292,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: c93bd73d9e1ab49b07e139582187f1c3760ee2cf0163b6288fab2ae210e39e59240a26284e7e5d29bec851255ef4b43c51642c882fa5a94e16ce7cb906deeb47 + checksum: ec3662f64895fae408440a997f87299e374c9d9f911f77b880bab46402f52221c7836bdf101fc2556338d07fc7cb86da50661f944eb1d1041a8361a5b9247876 languageName: node linkType: hard @@ -11734,9 +13481,9 @@ __metadata: linkType: hard "yaml@npm:^1.7.2": - version: 1.10.2 - resolution: "yaml@npm:1.10.2" - checksum: ce4ada136e8a78a0b08dc10b4b900936912d15de59905b2bf415b4d33c63df1d555d23acb2a41b23cf9fb5da41c256441afca3d6509de7247daa062fd2c5ea5f + version: 1.10.3 + resolution: "yaml@npm:1.10.3" + checksum: 6a2dd3582f4fbcc8d0e32dc26d1a42f72a901eb6ae8fad616bd720514b11a53a64eabc21dba97fbcd951c7c0e1963502313789d93a753e7786e7452376498be5 languageName: node linkType: hard diff --git a/packages/e2e-react/.gitignore b/packages/e2e-react/.gitignore index b88c8135ab..f79792577c 100644 --- a/packages/e2e-react/.gitignore +++ b/packages/e2e-react/.gitignore @@ -26,3 +26,4 @@ dist-ssr /playwright-report/ /blob-report/ /playwright/.cache/ +/tests/.auth/ diff --git a/packages/e2e-react/package.json b/packages/e2e-react/package.json index 1e34c0276c..d9d96ffb4c 100644 --- a/packages/e2e-react/package.json +++ b/packages/e2e-react/package.json @@ -14,14 +14,14 @@ }, "dependencies": { "@embeddedchat/react": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@playwright/test": "^1.41.2", "@types/node": "^20.11.19", - "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.19", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", diff --git a/packages/e2e-react/playwright.config.ts b/packages/e2e-react/playwright.config.ts index a91deee96a..9bbe41df51 100644 --- a/packages/e2e-react/playwright.config.ts +++ b/packages/e2e-react/playwright.config.ts @@ -73,5 +73,8 @@ export default defineConfig({ command: 'yarn dev', url: 'http://127.0.0.1:5173', reuseExistingServer: !process.env.CI, + timeout: 120000, + stdout: 'pipe', + stderr: 'pipe', }, }); diff --git a/packages/e2e-react/vite.config.ts b/packages/e2e-react/vite.config.ts index 028287cd5a..d89dc27b4a 100644 --- a/packages/e2e-react/vite.config.ts +++ b/packages/e2e-react/vite.config.ts @@ -6,5 +6,8 @@ export default defineConfig({ plugins: [react()], define: { 'process.env': {} - } + }, + server: { + host: '127.0.0.1', + }, }) diff --git a/packages/htmlembed/package.json b/packages/htmlembed/package.json index 82beecd43a..9d433d5554 100644 --- a/packages/htmlembed/package.json +++ b/packages/htmlembed/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "@embeddedchat/react": "workspace:^", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { "@vitejs/plugin-react": "^3.1.0", diff --git a/packages/layout_editor/package.json b/packages/layout_editor/package.json index e8e9827e39..5cc6d09171 100644 --- a/packages/layout_editor/package.json +++ b/packages/layout_editor/package.json @@ -14,9 +14,9 @@ "@dnd-kit/sortable": "^8.0.0", "@embeddedchat/markups": "workspace:^", "@embeddedchat/ui-elements": "workspace:^", - "react": "^18.2.0", + "react": "^19.0.0", "react-color": "^2.19.3", - "react-dom": "^18.2.0", + "react-dom": "^19.0.0", "react-resizable-panels": "^2.0.20", "react-syntax-highlighter": "^15.5.0" }, diff --git a/packages/markups/package.json b/packages/markups/package.json index ab60f023f7..25b6b57032 100644 --- a/packages/markups/package.json +++ b/packages/markups/package.json @@ -53,8 +53,8 @@ "lint-staged": "^12.4.2", "npm-run-all": "^4.1.5", "prettier": "^2.8.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rimraf": "^5.0.1", "rollup": "^2.70.1", "rollup-plugin-analyzer": "^4.0.0", @@ -67,8 +67,8 @@ "typescript": "^5.5.3" }, "peerDependencies": { - "react": ">=17.0.2 <19.0.0", - "react-dom": ">=17.0.2 <19.0.0" + "react": ">=19.0.0 <20.0.0", + "react-dom": ">=19.0.0 <20.0.0" }, "dependencies": { "@embeddedchat/ui-elements": "workspace:^", diff --git a/packages/react/.storybook/main.js b/packages/react/.storybook/main.js index 7c981da605..0db593a3da 100644 --- a/packages/react/.storybook/main.js +++ b/packages/react/.storybook/main.js @@ -5,15 +5,7 @@ const config = { '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', - { - name: '@storybook/addon-styling', - options: { - sass: { - // Require your Sass preprocessor here - implementation: require('sass'), - }, - }, - }, + '@storybook/addon-webpack5-compiler-babel', ], framework: { name: '@storybook/react-webpack5', diff --git a/packages/react/package.json b/packages/react/package.json index 82cc802682..9c60c6a4f4 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -13,8 +13,8 @@ ], "scripts": { "prebuild": "rimraf dist", - "build": "rollup -c --context=window --environment NODE_ENV:production", - "watch": "rollup -c --watch --context=window", + "build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 rollup -c --context=window --environment NODE_ENV:production", + "watch": "cross-env NODE_OPTIONS=--max-old-space-size=4096 rollup -c --watch --context=window", "test:lint": "eslint src/**/*.js", "format": "prettier --write 'src/' ", "format:check": "prettier --check 'src/' ", @@ -37,14 +37,14 @@ "@rollup/plugin-commonjs": "^21.0.2", "@rollup/plugin-node-resolve": "^13.1.3", "@rollup/plugin-replace": "^5.0.2", - "@storybook/addon-essentials": "^7.0.26", - "@storybook/addon-interactions": "^7.0.26", - "@storybook/addon-links": "^7.0.26", - "@storybook/addon-styling": "^1.3.6", - "@storybook/blocks": "^7.0.26", - "@storybook/react": "^7.0.26", - "@storybook/react-webpack5": "^7.0.26", - "@storybook/testing-library": "^0.2.0", + "@storybook/addon-essentials": "^8.0.0", + "@storybook/addon-interactions": "^8.0.0", + "@storybook/addon-links": "^8.0.0", + "@storybook/addon-webpack5-compiler-babel": "^3.0.0", + "@storybook/blocks": "^8.0.0", + "@storybook/react": "^8.0.0", + "@storybook/react-webpack5": "^8.0.0", + "@storybook/test": "^8.0.0", "@testing-library/react": "^12.1.4", "babel-jest": "^27.5.1", "concurrently": "^7.2.0", @@ -65,8 +65,8 @@ "lint-staged": "^12.4.2", "npm-run-all": "^4.1.5", "prettier": "^2.8.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rimraf": "^5.0.1", "rollup": "^2.70.1", "rollup-plugin-analyzer": "^4.0.0", @@ -76,11 +76,11 @@ "rollup-plugin-terser": "^7.0.2", "sass": "^1.66.1", "schedule": "^0.4.0", - "storybook": "^7.0.26" + "storybook": "^8.0.0" }, "peerDependencies": { - "react": ">=17.0.2 <19.0.0", - "react-dom": ">=17.0.2 <19.0.0" + "react": ">=19.0.0 <20.0.0", + "react-dom": ">=19.0.0 <20.0.0" }, "dependencies": { "@embeddedchat/api": "workspace:^", diff --git a/packages/react/src/hooks/useShowCommands.js b/packages/react/src/hooks/useShowCommands.js index 1379f267c4..05115b6ce4 100644 --- a/packages/react/src/hooks/useShowCommands.js +++ b/packages/react/src/hooks/useShowCommands.js @@ -2,12 +2,11 @@ import { useCallback } from 'react'; const useShowCommands = (commands, setFilteredCommands, setShowCommandList) => useCallback( - async (e) => { + async (cursor, value) => { const getFilteredCommands = (cmd) => commands.filter((c) => c.command.startsWith(cmd.replace('/', ''))); - const cursor = e.target.selectionStart; - const tokens = e.target.value.slice(0, cursor).split(/\s+/); + const tokens = value.slice(0, cursor).split(/\s+/); if (tokens.length === 1 && tokens[0].startsWith('/')) { setFilteredCommands(getFilteredCommands(tokens[0])); diff --git a/packages/react/src/views/AttachmentPreview/PreviewType/audio.js b/packages/react/src/views/AttachmentPreview/PreviewType/audio.js index da4bbe9fff..7dc902b32b 100644 --- a/packages/react/src/views/AttachmentPreview/PreviewType/audio.js +++ b/packages/react/src/views/AttachmentPreview/PreviewType/audio.js @@ -5,7 +5,7 @@ import { Box } from '@embeddedchat/ui-elements'; function PreviewAudio({ previewURL }) { return ( - ); } diff --git a/packages/react/src/views/AttachmentPreview/PreviewType/image.js b/packages/react/src/views/AttachmentPreview/PreviewType/image.js index 5c2d3b4d8a..60573d75af 100644 --- a/packages/react/src/views/AttachmentPreview/PreviewType/image.js +++ b/packages/react/src/views/AttachmentPreview/PreviewType/image.js @@ -7,7 +7,7 @@ function PreviewImage({ previewURL }) { return ( { - RCInstance.auth.onAuthChange((user) => { - if (user) { - RCInstance.addMessageListener(addMessage); - RCInstance.addMessageDeleteListener(removeMessage); - RCInstance.addActionTriggeredListener(onActionTriggerResponse); - RCInstance.addUiInteractionListener(onActionTriggerResponse); - } - }); + if (isUserAuthenticated) { + RCInstance.addMessageListener(addMessage); + RCInstance.addMessageDeleteListener(removeMessage); + RCInstance.addActionTriggeredListener(onActionTriggerResponse); + RCInstance.addUiInteractionListener(onActionTriggerResponse); + } return () => { RCInstance.removeMessageListener(addMessage); @@ -161,28 +159,30 @@ const ChatBody = ({ RCInstance.removeActionTriggeredListener(onActionTriggerResponse); RCInstance.removeUiInteractionListener(onActionTriggerResponse); }; - }, [RCInstance, addMessage, removeMessage, onActionTriggerResponse]); + }, [ + RCInstance, + isUserAuthenticated, + addMessage, + removeMessage, + onActionTriggerResponse, + ]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { - if (user) { - getMessagesAndRoles(); - setHasMoreMessages(true); - } else { - getMessagesAndRoles(anonymousMode); - } - }); - }, [RCInstance, anonymousMode, getMessagesAndRoles]); + if (isUserAuthenticated) { + getMessagesAndRoles(); + setHasMoreMessages(true); + } else { + getMessagesAndRoles(anonymousMode); + } + }, [RCInstance, isUserAuthenticated, anonymousMode, getMessagesAndRoles]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { - if (user) { - fetchAndSetPermissions(); - } else { - permissionsRef.current = null; - } - }); - }, []); + if (isUserAuthenticated) { + fetchAndSetPermissions(); + } else { + permissionsRef.current = null; + } + }, [isUserAuthenticated, fetchAndSetPermissions, permissionsRef]); // Expose clearUnreadDivider function via ref for ChatInput to call useEffect(() => { diff --git a/packages/react/src/views/ChatHeader/ChatHeader.js b/packages/react/src/views/ChatHeader/ChatHeader.js index a310e538fd..c28e9ad39e 100644 --- a/packages/react/src/views/ChatHeader/ChatHeader.js +++ b/packages/react/src/views/ChatHeader/ChatHeader.js @@ -436,7 +436,12 @@ const ChatHeader = ({ {avatarUrl && ( - avatar + avatar )} {surfaceOptions.length > 0 && ( diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js index f6fb0e111f..0a8d0d7cd8 100644 --- a/packages/react/src/views/ChatInput/ChatInput.js +++ b/packages/react/src/views/ChatInput/ChatInput.js @@ -107,6 +107,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { isRecordingMessage, upsertMessage, replaceMessage, + removeMessage, clearQuoteMessages, threadId, deletedMessage, @@ -119,6 +120,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { replaceMessage: state.replaceMessage, threadId: state.threadMainMessage?._id, clearQuoteMessages: state.clearQuoteMessages, + removeMessage: state.removeMessage, deletedMessage: state.deletedMessage, })); @@ -161,20 +163,15 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { ); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { - if (user) { - RCInstance.getCommandsList() - .then((response) => setCommands(response.commands || [])) - .catch(console.error); - - RCInstance.getChannelMembers(isChannelPrivate) - .then((channelMembers) => - setMembersHandler(channelMembers.members || []) - ) - .catch(console.error); - } - }); - }, [RCInstance, isChannelPrivate, setMembersHandler]); + if (!isUserAuthenticated) return; + RCInstance.getCommandsList() + .then((response) => setCommands(response.commands || [])) + .catch(console.error); + + RCInstance.getChannelMembers(isChannelPrivate) + .then((channelMembers) => setMembersHandler(channelMembers.members || [])) + .catch(console.error); + }, [RCInstance, isUserAuthenticated, isChannelPrivate, setMembersHandler]); useEffect(() => { if (editMessage.attachments) { @@ -275,34 +272,26 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { } }; - const sendTypingStart = async () => { - try { - if (typingRef.current && messageRef.current.value?.length) { - return; - } - if (messageRef.current.value?.length) { - typingRef.current = true; - timerRef.current = setTimeout(() => { - typingRef.current = false; - }, [15000]); - await RCInstance.sendTypingStatus(username, true); - } else { - clearTimeout(timerRef.current); + const sendTypingStart = () => { + if (typingRef.current && messageRef.current.value?.length) { + return; + } + if (messageRef.current.value?.length) { + typingRef.current = true; + timerRef.current = setTimeout(() => { typingRef.current = false; - await RCInstance.sendTypingStatus(username, false); - } - } catch (e) { - console.error(e); + }, [15000]); + RCInstance.sendTypingStatus(username, true).catch(() => {}); + } else { + clearTimeout(timerRef.current); + typingRef.current = false; + RCInstance.sendTypingStatus(username, false).catch(() => {}); } }; - const sendTypingStop = async () => { - try { - typingRef.current = false; - await RCInstance.sendTypingStatus(username, false); - } catch (e) { - console.error(e); - } + const sendTypingStop = () => { + typingRef.current = false; + RCInstance.sendTypingStatus(username, false).catch(() => {}); }; const handleSendNewMessage = async (message) => { @@ -354,9 +343,12 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { ECOptions.enableThreads ? threadId : undefined ); - if (res.success) { + if (res?.success) { clearQuoteMessages(); - replaceMessage(pendingMessage, res.message); + replaceMessage(pendingMessage._id, res.message); + } else { + // If REST send failed, remove the pending message so it doesn't stay grey + removeMessage(pendingMessage._id); } }; @@ -448,7 +440,7 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { if (e !== null) { handleNewLine(e, false); searchMentionUser(message); - showCommands(e); + showCommands(e.target.selectionStart, e.target.value); searchEmoji(message); } }; @@ -724,7 +716,11 @@ const ChatInput = ({ scrollToBottom, clearUnreadDividerRef }) => { /> ) : null ) : ( - )} diff --git a/packages/react/src/views/EmbeddedChat.js b/packages/react/src/views/EmbeddedChat.js index f3b94c7b48..b2dc687247 100644 --- a/packages/react/src/views/EmbeddedChat.js +++ b/packages/react/src/views/EmbeddedChat.js @@ -134,7 +134,7 @@ const EmbeddedChat = (props) => { }, [RCInstance, auth, setIsLoginIn]); useEffect(() => { - RCInstance.auth.onAuthChange((user) => { + const handleAuthChange = (user) => { if (user) { RCInstance.connect() .then(() => { @@ -149,9 +149,16 @@ const EmbeddedChat = (props) => { }) .catch(console.error); } else { + // Close the DDP connection on logout so the next login gets a fresh connection. + RCInstance.close().catch(console.error); setIsUserAuthenticated(false); } - }); + }; + RCInstance.auth.onAuthChange(handleAuthChange); + + return () => { + RCInstance.auth.removeAuthListener(handleAuthChange); + }; }, [ RCInstance, setAuthenticatedName, diff --git a/packages/react/src/views/LoginForm/LoginForm.js b/packages/react/src/views/LoginForm/LoginForm.js index 1285e5e1eb..abf6e6e33a 100644 --- a/packages/react/src/views/LoginForm/LoginForm.js +++ b/packages/react/src/views/LoginForm/LoginForm.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState, useRef } from 'react'; import { css } from '@emotion/react'; import { GenericModal, @@ -13,8 +13,8 @@ import { useRCAuth } from '../../hooks/useRCAuth'; import styles from './LoginForm.styles'; export default function LoginForm() { - const [userOrEmail, setUserOrEmail] = useState(null); - const [password, setPassword] = useState(null); + const userRef = useRef(null); + const passRef = useRef(null); const [showPassword, setShowPassword] = useState(false); const [usernameError, setUsernameError] = useState(false); const [passwordError, setPasswordError] = useState(false); @@ -26,36 +26,40 @@ export default function LoginForm() { const { theme } = useTheme(); - useEffect(() => { - if (userOrEmail !== null && userOrEmail.trim() === '') { + const handleSubmit = (e) => { + if (e && e.preventDefault) e.preventDefault(); + const userOrEmail = userRef.current?.value || ''; + const password = passRef.current?.value || ''; + + let hasError = false; + if (userOrEmail.trim() === '') { setUsernameError(true); + hasError = true; } else { setUsernameError(false); } - if (password !== null && password.trim() === '') { + if (password.trim() === '') { setPasswordError(true); + hasError = true; } else { setPasswordError(false); } - }, [userOrEmail, password]); - const handleSubmit = () => { - if (!userOrEmail) setUserOrEmail(''); - if (!password) setPassword(''); - handleLogin(userOrEmail, password); + if (!hasError) { + handleLogin(userOrEmail, password); + } }; + const handleClose = () => { - setUserOrEmail(null); - setPassword(null); setIsLoginModalOpen(false); }; - const handleEdituserOrEmail = (e) => { - setUserOrEmail(e.target.value); + const handleEdituserOrEmail = () => { + if (usernameError) setUsernameError(false); }; - const handleEditPassword = (e) => { - setPassword(e.target.value); + const handleEditPassword = () => { + if (passwordError) setPasswordError(false); }; const handleTogglePassword = () => { setShowPassword(!showPassword); @@ -69,15 +73,18 @@ export default function LoginForm() { const fields = [ { label: 'Email or username', + ref: userRef, onChange: handleEdituserOrEmail, placeholder: 'example@example.com', error: usernameError, }, { label: 'Password', + ref: passRef, type: showPassword ? 'text' : 'password', onChange: handleEditPassword, error: passwordError, + autoComplete: 'new-password', }, ]; @@ -96,6 +103,7 @@ export default function LoginForm() { {message.file.name} @@ -320,7 +324,11 @@ export const MessageToolbox = ({ style={{ maxWidth: '100%', maxHeight: '200px' }} > Your browser does not support the video tag. @@ -328,7 +336,11 @@ export const MessageToolbox = ({ ) : message.file.type.startsWith('audio/') ? (