Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16.19.0
v22.16.0
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,9 @@
"dependencies": {
"dompurify": "^3.1.6",
"validator": "^13.15.15"
},
"resolutions": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
17 changes: 14 additions & 3 deletions packages/api/src/EmbeddedChatApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default class EmbeddedChatApi {
onUiInteractionCallbacks: ((data: any) => void)[];
typingUsers: string[];
auth: RocketChatAuth;
private _connectPromise: Promise<void> | null = null;

constructor(
host: string,
Expand Down Expand Up @@ -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() {
Comment thread
Spiral-Memory marked this conversation as resolved.
// 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({});
Expand All @@ -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) {
Expand Down Expand Up @@ -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
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/auth/src/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}

Expand Down
11 changes: 8 additions & 3 deletions packages/auth/src/RocketChatAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -72,6 +74,7 @@ class RocketChatAuth {
}
);
this.setUser(response.data);
this.notifyAuthListeners();
return this.currentUser;
}

Expand All @@ -92,6 +95,7 @@ class RocketChatAuth {
credentials
);
this.setUser(response.data);
this.notifyAuthListeners();
return this.currentUser;
}

Expand All @@ -107,6 +111,7 @@ class RocketChatAuth {
api: this.api,
});
this.setUser(response.data);
this.notifyAuthListeners();
return this.currentUser;
}

Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions packages/e2e-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions packages/htmlembed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions packages/layout_editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
8 changes: 4 additions & 4 deletions packages/markups/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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:^",
Expand Down
10 changes: 1 addition & 9 deletions packages/react/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
30 changes: 15 additions & 15 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/' ",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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:^",
Expand Down
5 changes: 2 additions & 3 deletions packages/react/src/hooks/useShowCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Box } from '@embeddedchat/ui-elements';
function PreviewAudio({ previewURL }) {
return (
<Box>
<audio src={previewURL} width="100%" controls />
<audio src={previewURL || null} width="100%" controls />
</Box>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function PreviewImage({ previewURL }) {
return (
<Box>
<img
src={previewURL}
src={previewURL || null}
style={{
maxWidth: '90%',
objectFit: 'contain',
Expand Down
46 changes: 20 additions & 26 deletions packages/react/src/views/ChatBody/ChatBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,43 +146,37 @@ const ChatBody = ({
);

useEffect(() => {
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);
RCInstance.removeMessageDeleteListener(removeMessage);
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(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/views/ChatHeader/ChatHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ const ChatHeader = ({
</Box>
<Box css={styles.chatHeaderIconRow}>
{avatarUrl && (
<img width="20px" height="20px" src={avatarUrl} alt="avatar" />
<img width="20px" height="20px" src={avatarUrl || null} alt="avatar" />
)}

{surfaceOptions.length > 0 && (
Expand Down
Loading