diff --git a/.docker/Dockerfile.rhel b/.docker/Dockerfile.rhel
index 646c83d64c15..850a879c7a89 100644
--- a/.docker/Dockerfile.rhel
+++ b/.docker/Dockerfile.rhel
@@ -1,6 +1,6 @@
FROM registry.access.redhat.com/ubi8/nodejs-12
-ENV RC_VERSION 4.5.0
+ENV RC_VERSION 4.5.1
MAINTAINER buildmaster@rocket.chat
diff --git a/.github/history.json b/.github/history.json
index 6d6052bb6dcd..6fb618dfbcc9 100644
--- a/.github/history.json
+++ b/.github/history.json
@@ -71306,6 +71306,157 @@
"'5.0'"
],
"pull_requests": []
+ },
+ "4.5.1": {
+ "node_version": "14.18.3",
+ "npm_version": "6.14.15",
+ "apps_engine_version": "1.31.0",
+ "mongo_versions": [
+ "'3.6'",
+ "'4.0'",
+ "'4.2'",
+ "'4.4'",
+ "'5.0'"
+ ],
+ "pull_requests": [
+ {
+ "pr": "24760",
+ "title": "[FIX] Apple login script being loaded even when Apple Login is disabled.",
+ "userLogin": "pierre-lehnen-rc",
+ "milestone": "4.5.1",
+ "contributors": [
+ "pierre-lehnen-rc"
+ ]
+ },
+ {
+ "pr": "24754",
+ "title": "Chore: Update Livechat",
+ "userLogin": "MartinSchoeler",
+ "milestone": "4.5.1",
+ "contributors": [
+ "MartinSchoeler"
+ ]
+ },
+ {
+ "pr": "24683",
+ "title": "[FIX] no id of room closer in livechat-close message",
+ "userLogin": "cuonghuunguyen",
+ "milestone": "4.5.1",
+ "contributors": [
+ null
+ ]
+ },
+ {
+ "pr": "23795",
+ "title": "[FIX] Reload roomslist after successful deletion of a room from admin panel.",
+ "userLogin": "Aman-Maheshwari",
+ "description": "Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`.\r\nThis allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent.\r\n\r\nAlso added a succes toast message after the successful deletion of room.",
+ "milestone": "4.5.1",
+ "contributors": [
+ "Aman-Maheshwari",
+ "web-flow",
+ "tassoevan"
+ ]
+ },
+ {
+ "pr": "24743",
+ "title": "[FIX] System messages are sent when adding or removing a group from a team",
+ "userLogin": "matheusbsilva137",
+ "description": "- Do not send system messages when adding or removing a new or existing _group_ from a team.",
+ "milestone": "4.5.1",
+ "contributors": [
+ "matheusbsilva137"
+ ]
+ },
+ {
+ "pr": "24737",
+ "title": "[FIX] Typo and placeholder on wrap up call modal",
+ "userLogin": "MartinSchoeler",
+ "milestone": "4.5.1",
+ "contributors": [
+ "MartinSchoeler"
+ ]
+ },
+ {
+ "pr": "24680",
+ "title": "[FIX] Show only available agents on extension association modal",
+ "userLogin": "KevLehman",
+ "milestone": "4.5.1",
+ "contributors": [
+ "KevLehman",
+ "tiagoevanp"
+ ]
+ },
+ {
+ "pr": "24607",
+ "title": "[FIX] VoIP Enable/Disable setting on CallContext/CallProvider Notifications",
+ "userLogin": "tiagoevanp",
+ "milestone": "4.5.1",
+ "contributors": [
+ "tiagoevanp",
+ "web-flow",
+ "tassoevan"
+ ]
+ },
+ {
+ "pr": "24677",
+ "title": "[FIX] Components for user search",
+ "userLogin": "juliajforesti",
+ "milestone": "4.5.1",
+ "contributors": [
+ "juliajforesti",
+ "tassoevan",
+ "web-flow"
+ ]
+ },
+ {
+ "pr": "24657",
+ "title": "[FIX] Voip Stream Reinitialization Error",
+ "userLogin": "amolghode1981",
+ "milestone": "4.5.1",
+ "contributors": [
+ "amolghode1981"
+ ]
+ },
+ {
+ "pr": "24696",
+ "title": "[FIX] Room's message count not being incremented on import",
+ "userLogin": "matheusbsilva137",
+ "description": "- Fix rooms' message counter not being incremented on message import.",
+ "milestone": "4.5.1",
+ "contributors": [
+ "matheusbsilva137"
+ ]
+ },
+ {
+ "pr": "24674",
+ "title": "[FIX] Missing username on messages imported from Slack",
+ "userLogin": "matheusbsilva137",
+ "description": "- Fix missing sender's username on messages imported from Slack.",
+ "milestone": "4.5.1",
+ "contributors": [
+ "matheusbsilva137"
+ ]
+ },
+ {
+ "pr": "24590",
+ "title": "[FIX] Duplicated 'name' log key",
+ "userLogin": "sampaiodiego",
+ "milestone": "4.5.1",
+ "contributors": [
+ "sampaiodiego"
+ ]
+ },
+ {
+ "pr": "24661",
+ "title": "[FIX] Typo in wrap-up term",
+ "userLogin": "renatobecker",
+ "milestone": "4.5.1",
+ "contributors": [
+ "renatobecker"
+ ]
+ }
+ ]
}
}
}
\ No newline at end of file
diff --git a/.snapcraft/resources/prepareRocketChat b/.snapcraft/resources/prepareRocketChat
index d89666a4a506..15fa6f07861d 100755
--- a/.snapcraft/resources/prepareRocketChat
+++ b/.snapcraft/resources/prepareRocketChat
@@ -1,6 +1,6 @@
#!/bin/bash
-curl -SLf "https://releases.rocket.chat/4.5.0/download/" -o rocket.chat.tgz
+curl -SLf "https://releases.rocket.chat/4.5.1/download/" -o rocket.chat.tgz
tar xf rocket.chat.tgz --strip 1
diff --git a/.snapcraft/snap/snapcraft.yaml b/.snapcraft/snap/snapcraft.yaml
index 41f386057ee0..92087b132cfc 100644
--- a/.snapcraft/snap/snapcraft.yaml
+++ b/.snapcraft/snap/snapcraft.yaml
@@ -7,7 +7,7 @@
# 5. `snapcraft snap`
name: rocketchat-server
-version: 4.5.0
+version: 4.5.1
summary: Rocket.Chat server
description: Have your own Slack like online chat, built with Meteor. https://rocket.chat/
confinement: strict
diff --git a/HISTORY.md b/HISTORY.md
index 0326008d089a..7de8158ee519 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,4 +1,78 @@
+# 4.5.1
+`2022-03-09 · 13 🐛 · 1 🔍 · 11 👩💻👨💻`
+
+### Engine versions
+- Node: `14.18.3`
+- NPM: `6.14.15`
+- MongoDB: `'3.6', '4.0', '4.2', '4.4', '5.0'`
+- Apps-Engine: `1.31.0`
+
+### 🐛 Bug fixes
+
+
+- Apple login script being loaded even when Apple Login is disabled. ([#24760](https://github.com/RocketChat/Rocket.Chat/pull/24760))
+
+- Components for user search ([#24677](https://github.com/RocketChat/Rocket.Chat/pull/24677))
+
+- Duplicated 'name' log key ([#24590](https://github.com/RocketChat/Rocket.Chat/pull/24590))
+
+- Missing username on messages imported from Slack ([#24674](https://github.com/RocketChat/Rocket.Chat/pull/24674))
+
+ - Fix missing sender's username on messages imported from Slack.
+
+- no id of room closer in livechat-close message ([#24683](https://github.com/RocketChat/Rocket.Chat/pull/24683))
+
+- Reload roomslist after successful deletion of a room from admin panel. ([#23795](https://github.com/RocketChat/Rocket.Chat/pull/23795) by [@Aman-Maheshwari](https://github.com/Aman-Maheshwari))
+
+ Removed the logic for calling the `rooms.adminRooms` endPoint from the `RoomsTable` Component and moved it to its parent component `RoomsPage`.
+ This allows to call the endPoint `rooms.adminRooms` from `EditRoomContextBar` Component which is also has `RoomPage` Component as its parent.
+
+ Also added a succes toast message after the successful deletion of room.
+
+- Room's message count not being incremented on import ([#24696](https://github.com/RocketChat/Rocket.Chat/pull/24696))
+
+ - Fix rooms' message counter not being incremented on message import.
+
+- Show only available agents on extension association modal ([#24680](https://github.com/RocketChat/Rocket.Chat/pull/24680))
+
+- System messages are sent when adding or removing a group from a team ([#24743](https://github.com/RocketChat/Rocket.Chat/pull/24743))
+
+ - Do not send system messages when adding or removing a new or existing _group_ from a team.
+
+- Typo and placeholder on wrap up call modal ([#24737](https://github.com/RocketChat/Rocket.Chat/pull/24737))
+
+- Typo in wrap-up term ([#24661](https://github.com/RocketChat/Rocket.Chat/pull/24661))
+
+- VoIP Enable/Disable setting on CallContext/CallProvider Notifications ([#24607](https://github.com/RocketChat/Rocket.Chat/pull/24607))
+
+- Voip Stream Reinitialization Error ([#24657](https://github.com/RocketChat/Rocket.Chat/pull/24657))
+
+
+🔍 Minor changes
+
+
+- Chore: Update Livechat ([#24754](https://github.com/RocketChat/Rocket.Chat/pull/24754))
+
+
+
+### 👩💻👨💻 Contributors 😍
+
+- [@Aman-Maheshwari](https://github.com/Aman-Maheshwari)
+
+### 👩💻👨💻 Core Team 🤓
+
+- [@KevLehman](https://github.com/KevLehman)
+- [@MartinSchoeler](https://github.com/MartinSchoeler)
+- [@amolghode1981](https://github.com/amolghode1981)
+- [@juliajforesti](https://github.com/juliajforesti)
+- [@matheusbsilva137](https://github.com/matheusbsilva137)
+- [@pierre-lehnen-rc](https://github.com/pierre-lehnen-rc)
+- [@renatobecker](https://github.com/renatobecker)
+- [@sampaiodiego](https://github.com/sampaiodiego)
+- [@tassoevan](https://github.com/tassoevan)
+- [@tiagoevanp](https://github.com/tiagoevanp)
+
# 4.5.0
`2022-02-28 · 3 🎉 · 15 🚀 · 19 🐛 · 72 🔍 · 30 👩💻👨💻`
diff --git a/app/api/server/v1/voip/omnichannel.ts b/app/api/server/v1/voip/omnichannel.ts
index 163f1f19b5e8..a84831de87c1 100644
--- a/app/api/server/v1/voip/omnichannel.ts
+++ b/app/api/server/v1/voip/omnichannel.ts
@@ -72,6 +72,9 @@ API.v1.addRoute(
let user: IUser | null = null;
if (!isUserAndExtensionParams(this.bodyParams)) {
+ if (!this.bodyParams.username) {
+ return API.v1.notFound();
+ }
user = await Users.findOneByAgentUsername(this.bodyParams.username, {
projection: {
_id: 1,
@@ -79,6 +82,9 @@ API.v1.addRoute(
},
});
} else {
+ if (!this.bodyParams.userId) {
+ return API.v1.notFound();
+ }
user = await Users.findOneAgentById(this.bodyParams.userId, {
projection: {
_id: 1,
@@ -167,33 +173,22 @@ API.v1.addRoute(
if (!extensions) {
return API.v1.failure('Error in allocated extensions');
}
- return API.v1.success({ extensions });
+ return API.v1.success({ extensions: extensions.map((e) => e.extension) });
}
case 'available': {
let user: IUser | null = null;
if (!isUserIdndTypeParams(this.queryParams)) {
user = await Users.findOneByAgentUsername(this.queryParams.username, {
- projection: { _id: 1 },
+ projection: { _id: 1, extension: 1 },
});
} else {
user = await Users.findOneAgentById(this.queryParams.userId, {
- projection: { _id: 1 },
+ projection: { _id: 1, extension: 1 },
});
}
- if (!user) {
- return API.v1.notFound('User not found');
- }
-
- const extension = await Users.getVoipExtensionByUserId(user._id, {
- projection: {
- _id: 1,
- username: 1,
- extension: 1,
- },
- });
const freeExt = await LivechatVoip.getFreeExtensions();
- const extensions = extension ? [extension.extension, ...freeExt] : freeExt;
+ const extensions = user?.extension ? [user.extension, ...freeExt] : freeExt;
return API.v1.success({ extensions });
}
default:
@@ -221,3 +216,24 @@ API.v1.addRoute(
},
},
);
+
+API.v1.addRoute(
+ 'omnichannel/agents/available',
+ { authRequired: true, permissionsRequired: ['manage-agent-extension-association'] },
+ {
+ async get() {
+ const { offset, count } = this.getPaginationItems();
+ const { sort } = this.parseJsonQuery();
+ const { text, includeExtension = '' } = this.queryParams;
+
+ const { agents, total } = await LivechatVoip.getAvailableAgents(includeExtension, text, count, offset, sort);
+
+ return API.v1.success({
+ agents,
+ offset,
+ count,
+ total,
+ });
+ },
+ },
+);
diff --git a/app/apps/server/bridges/livechat.ts b/app/apps/server/bridges/livechat.ts
index 3c79c709fbb7..3c58c649d55e 100644
--- a/app/apps/server/bridges/livechat.ts
+++ b/app/apps/server/bridges/livechat.ts
@@ -111,7 +111,7 @@ export class AppLivechatBridge extends LivechatBridge {
protected async closeRoom(room: ILivechatRoom, comment: string, closer: IUser | undefined, appId: string): Promise {
this.orch.debugLog(`The App ${appId} is closing a livechat room.`);
- const user = closer && this.orch.getConverters()?.get('users').convertById(closer.id);
+ const user = closer && this.orch.getConverters()?.get('users').convertToRocketChat(closer);
const visitor = this.orch.getConverters()?.get('visitors').convertAppVisitor(room.visitor);
const closeData: any = {
diff --git a/app/importer/server/classes/ImportDataConverter.ts b/app/importer/server/classes/ImportDataConverter.ts
index 95d79f36df46..4bfa7ac5b46f 100644
--- a/app/importer/server/classes/ImportDataConverter.ts
+++ b/app/importer/server/classes/ImportDataConverter.ts
@@ -263,7 +263,7 @@ export class ImportDataConverter {
}
if (userData.importIds.length) {
- this.addUserToCache(userData.importIds[0], existingUser._id, existingUser.username);
+ this.addUserToCache(userData.importIds[0], existingUser._id, existingUser.username || userData.username);
}
}
diff --git a/app/lib/server/functions/createRoom.js b/app/lib/server/functions/createRoom.js
index 96cd49c579d0..50719f2553c2 100644
--- a/app/lib/server/functions/createRoom.js
+++ b/app/lib/server/functions/createRoom.js
@@ -131,12 +131,11 @@ export const createRoom = function (type, name, owner, members = [], readOnly, {
addUserRoles(owner._id, ['owner'], room._id);
- if (room.teamId) {
- const team = Promise.await(Team.getOneById(room.teamId));
- Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, owner);
- }
-
if (type === 'c') {
+ if (room.teamId) {
+ const team = Promise.await(Team.getOneById(room.teamId));
+ Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, owner);
+ }
Meteor.defer(() => {
callbacks.run('afterCreateChannel', owner, room);
});
diff --git a/app/lib/server/functions/insertMessage.js b/app/lib/server/functions/insertMessage.js
index a9c237b78858..13595390ca86 100644
--- a/app/lib/server/functions/insertMessage.js
+++ b/app/lib/server/functions/insertMessage.js
@@ -1,4 +1,4 @@
-import { Messages } from '../../../models/server';
+import { Messages, Rooms } from '../../../models/server';
import { validateMessage, prepareMessageObject } from './sendMessage';
import { parseUrlsInMessage } from './parseUrlsInMessage';
@@ -14,6 +14,7 @@ export const insertMessage = function (user, message, rid, upsert = false) {
if (message._id && upsert) {
const { _id } = message;
delete message._id;
+ const existingMessage = Messages.findOneById(_id);
Messages.upsert(
{
_id,
@@ -21,9 +22,13 @@ export const insertMessage = function (user, message, rid, upsert = false) {
},
message,
);
+ if (!existingMessage) {
+ Rooms.incMsgCountById(rid, 1);
+ }
message._id = _id;
} else {
message._id = Messages.insert(message);
+ Rooms.incMsgCountById(rid, 1);
}
return message;
diff --git a/app/models/server/raw/Users.js b/app/models/server/raw/Users.js
index d4e284252a11..03748aeaa323 100644
--- a/app/models/server/raw/Users.js
+++ b/app/models/server/raw/Users.js
@@ -967,4 +967,22 @@ export class UsersRaw extends BaseRaw {
};
return this.update(query, update);
}
+
+ getAvailableAgentsIncludingExt(includeExt, text, options) {
+ const query = {
+ roles: { $in: ['livechat-agent', 'livechat-manager', 'livechat-monitor'] },
+ $and: [
+ { $or: [...(includeExt ? [{ extension: includeExt }] : []), { extension: { $exists: false } }] },
+ ...(text && text.trim()
+ ? [
+ {
+ $or: [{ username: escapeRegExp(text) }, { name: escapeRegExp(text) }],
+ },
+ ]
+ : []),
+ ],
+ };
+
+ return this.find(query, options);
+ }
}
diff --git a/app/utils/rocketchat.info b/app/utils/rocketchat.info
index 5a4ca2b3d08a..a14d9200f10d 100644
--- a/app/utils/rocketchat.info
+++ b/app/utils/rocketchat.info
@@ -1,3 +1,3 @@
{
- "version": "4.5.0"
+ "version": "4.5.1"
}
diff --git a/client/components/AutoCompleteAgentWithoutExtension.tsx b/client/components/AutoCompleteAgentWithoutExtension.tsx
new file mode 100644
index 000000000000..079a68192039
--- /dev/null
+++ b/client/components/AutoCompleteAgentWithoutExtension.tsx
@@ -0,0 +1,69 @@
+import { PaginatedSelectFiltered } from '@rocket.chat/fuselage';
+import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import React, { FC, memo, useMemo, useState } from 'react';
+
+import { ILivechatAgent } from '../../definition/ILivechatAgent';
+import { useRecordList } from '../hooks/lists/useRecordList';
+import { AsyncStatePhase } from '../lib/asyncState';
+import { useAvailableAgentsList } from './Omnichannel/hooks/useAvailableAgentsList';
+
+type AutoCompleteAgentProps = {
+ onChange: (value: string) => void;
+ empty: boolean;
+ haveAll?: boolean;
+ value?: string;
+ currentExtension?: string;
+};
+
+const AutoCompleteAgentWithoutExtension: FC = (props) => {
+ const { value, currentExtension, onChange = (): void => undefined, haveAll = false } = props;
+ const [agentsFilter, setAgentsFilter] = useState('');
+
+ const debouncedAgentsFilter = useDebouncedValue(agentsFilter as string, 500);
+
+ const { itemsList: AgentsList, loadMoreItems: loadMoreAgents } = useAvailableAgentsList(
+ useMemo(
+ () => ({ text: debouncedAgentsFilter, includeExtension: currentExtension, haveAll }),
+ [currentExtension, debouncedAgentsFilter, haveAll],
+ ),
+ );
+ const { phase: agentsPhase, items: agentsItems, itemCount: agentsTotal } = useRecordList(AgentsList);
+ const sortedByName = agentsItems
+ .sort((a, b) => {
+ if (value === 'all') {
+ return -1;
+ }
+
+ if (a?.username?.localeCompare(b?.username || '')) {
+ return 1;
+ }
+ if (b?.username?.localeCompare(b?.username || '')) {
+ return -1;
+ }
+
+ return 0;
+ })
+ .map((agent): ILivechatAgent & { label: string; value: string } => ({
+ ...agent,
+ label: agent?.username || '',
+ value: agent?.username || '',
+ }));
+
+ return (
+ {
+ setAgentsFilter(value);
+ }}
+ options={sortedByName}
+ endReached={
+ agentsPhase === AsyncStatePhase.LOADING ? (): void => undefined : (start): void => loadMoreAgents(start, Math.min(50, agentsTotal))
+ }
+ />
+ );
+};
+
+export default memo(AutoCompleteAgentWithoutExtension);
diff --git a/client/components/Omnichannel/hooks/useAvailableAgentsList.ts b/client/components/Omnichannel/hooks/useAvailableAgentsList.ts
new file mode 100644
index 000000000000..81c16785c65d
--- /dev/null
+++ b/client/components/Omnichannel/hooks/useAvailableAgentsList.ts
@@ -0,0 +1,65 @@
+import { useCallback, useState } from 'react';
+
+import { ILivechatAgent } from '../../../../definition/ILivechatAgent';
+import { useEndpoint } from '../../../contexts/ServerContext';
+import { useScrollableRecordList } from '../../../hooks/lists/useScrollableRecordList';
+import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
+import { RecordList } from '../../../lib/lists/RecordList';
+
+type AgentsListOptions = {
+ text: string;
+ includeExtension?: string;
+};
+
+export const useAvailableAgentsList = (
+ options: AgentsListOptions,
+): {
+ itemsList: RecordList;
+ initialItemCount: number;
+ reload: () => void;
+ loadMoreItems: (start: number, end: number) => void;
+} => {
+ const [itemsList, setItemsList] = useState(() => new RecordList());
+ const reload = useCallback(() => setItemsList(new RecordList()), []);
+ const endpoint = 'omnichannel/agents/available';
+
+ const getAgents = useEndpoint('GET', endpoint);
+
+ useComponentDidUpdate(() => {
+ options && reload();
+ }, [options, reload]);
+
+ const fetchData = useCallback(
+ async (start, end) => {
+ const { agents, total } = await getAgents({
+ ...(options.text && { text: options.text }),
+ ...(options.includeExtension && { includeExtension: options.includeExtension }),
+ offset: start,
+ count: end + start,
+ sort: `{ "name": 1 }`,
+ });
+
+ const items = agents.map((agent: any) => {
+ agent._updatedAt = new Date(agent._updatedAt);
+ agent.label = agent.username;
+ agent.value = agent._id;
+ return agent;
+ });
+
+ return {
+ items,
+ itemCount: total,
+ };
+ },
+ [getAgents, options.includeExtension, options.text],
+ );
+
+ const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25);
+
+ return {
+ reload,
+ itemsList,
+ loadMoreItems,
+ initialItemCount,
+ };
+};
diff --git a/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.js b/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.js
index c1358530c191..482fd6e39fbc 100644
--- a/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.js
+++ b/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.js
@@ -1,41 +1,59 @@
import { MultiSelectFiltered, Box, Option, OptionAvatar, OptionContent, OptionDescription, Chip, CheckBox } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
-import React, { memo, useMemo, useState } from 'react';
+import React, { memo, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointData } from '../../hooks/useEndpointData';
+import { AsyncStatePhase } from '../../lib/asyncState';
import UserAvatar from '../avatar/UserAvatar';
const query = (term = '') => ({ selector: JSON.stringify({ term }) });
-const UserAutoCompleteMultiple = (props) => {
+const UserAutoCompleteMultiple = ({ valueIsId = false, ...props }) => {
+ const t = useTranslation();
const [filter, setFilter] = useState('');
+ const [labelData, setLabelData] = useState({});
const debouncedFilter = useDebouncedValue(filter, 1000);
- const { value: data } = useEndpointData(
+ const { value: data, phase } = useEndpointData(
'users.autocomplete',
useMemo(() => query(debouncedFilter), [debouncedFilter]),
);
- const options = useMemo(() => (data && data.items.map((user) => [user.username, user.name])) || [], [data]);
-
- const renderItem = ({ value, label, selected, ...props }) => (
-
+ const options = useMemo(
+ () => (data && data.items.map((user) => [valueIsId ? user._id : user.username, user.name])) || [],
+ [data, valueIsId],
);
- const renderSelected = ({ value, onMouseDown }) => (
-
-
-
- {value}
-
-
- );
+ useEffect(() => {
+ const newLabelData = Object.fromEntries((data && data.items.map((user) => [user._id, user.username])) || []);
+ setLabelData((labelData) => ({ ...labelData, ...newLabelData }));
+ }, [data]);
+
+ const renderItem = ({ value, label, selected, ...props }) => {
+ const username = valueIsId ? labelData[value] : value;
+ return (
+
+ );
+ };
+
+ const renderSelected = ({ value, onMouseDown }) => {
+ const username = valueIsId ? labelData[value] : value;
+ return (
+
+
+
+ {username}
+
+
+ );
+ };
return (
{
renderSelected={renderSelected}
renderItem={renderItem}
addonIcon='magnifier'
+ customEmpty={phase === AsyncStatePhase.LOADING ? t('Loading') : t('None')} // TODO: add proper empty state
/>
);
};
diff --git a/client/components/voip/modal/WrapUpCallModal.tsx b/client/components/voip/modal/WrapUpCallModal.tsx
index 0ac7e357ee8d..1dbd42e23579 100644
--- a/client/components/voip/modal/WrapUpCallModal.tsx
+++ b/client/components/voip/modal/WrapUpCallModal.tsx
@@ -44,14 +44,14 @@ export const WrapUpCallModal = (): ReactElement => {
return (
- {t('Wrap_Up_the_Call')}
+ {t('Wrap_up_the_call')}
{t('Notes')}
-
+
{t('These_notes_will_be_available_in_the_call_summary')}
diff --git a/client/lib/voip/Stream.ts b/client/lib/voip/Stream.ts
index d8d33b95927f..5cb078d58cb2 100644
--- a/client/lib/voip/Stream.ts
+++ b/client/lib/voip/Stream.ts
@@ -61,7 +61,8 @@ export default class Stream {
if (this.renderingMediaElement) {
// Someone already has setup the stream and initializing it once again
// Clear the existing stream object
- this.renderingMediaElement.srcObject == null;
+ this.renderingMediaElement.pause();
+ this.renderingMediaElement.srcObject = null;
}
this.renderingMediaElement = rmElement;
}
diff --git a/client/providers/CallProvider/CallProvider.tsx b/client/providers/CallProvider/CallProvider.tsx
index 1b3307b2a1ae..14ec7d084bc6 100644
--- a/client/providers/CallProvider/CallProvider.tsx
+++ b/client/providers/CallProvider/CallProvider.tsx
@@ -4,7 +4,6 @@ import { createPortal } from 'react-dom';
import { OutgoingByeRequest } from 'sip.js/lib/core';
import { CustomSounds } from '../../../app/custom-sounds/client';
-import { Notifications } from '../../../app/notifications/client';
import { getUserPreference } from '../../../app/utils/client';
import { IVoipRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';
@@ -12,7 +11,7 @@ import { WrapUpCallModal } from '../../components/voip/modal/WrapUpCallModal';
import { CallContext, CallContextValue } from '../../contexts/CallContext';
import { useSetModal } from '../../contexts/ModalContext';
import { useRoute } from '../../contexts/RouterContext';
-import { useEndpoint } from '../../contexts/ServerContext';
+import { useEndpoint, useStream } from '../../contexts/ServerContext';
import { useSetting } from '../../contexts/SettingsContext';
import { useUser } from '../../contexts/UserContext';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
@@ -33,8 +32,8 @@ const stopRingback = (): void => {
export const CallProvider: FC = ({ children }) => {
const voipEnabled = useSetting('VoIP_Enabled');
+ const subscribeToNotifyUser = useStream('notify-user');
- // TODO: Test Settings and return false if its disabled (based on the settings)
const result = useVoipClient();
const user = useUser();
@@ -52,114 +51,131 @@ export const CallProvider: FC = ({ children }) => {
setModal();
}, [setModal]);
- const handleAgentConnected = useCallback(
- (queue: { queuename: string; queuedcalls: string; waittimeinqueue: string }): void => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.callPickedup(queue);
- setQueueCounter(queueAggregator.getCallWaitingCount().toString());
- }
- },
- [result],
- );
+ useEffect(() => {
+ if (!voipEnabled || !user) {
+ return;
+ }
- const handleAgentCalled = useCallback(
- (queueInfo: { queuename: string; callerId: { id: string; name: string } }): void => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.callRinging(queueInfo);
- }
- },
- [result],
- );
+ if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
+ return;
+ }
- const handleMemberAdded = useCallback(
- (queue: { queuename: string; queuedcalls: string }): void => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.memberAdded(queue);
- setQueueCounter(queueAggregator.getCallWaitingCount().toString());
- }
- },
- [result],
- );
+ const queueAggregator = result.voipClient.getAggregator();
+ if (!queueAggregator) {
+ return;
+ }
- const handleMemberRemoved = useCallback(
- (queue: { queuename: string; queuedcalls: string }): void => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.memberRemoved(queue);
- setQueueCounter(queueAggregator.getCallWaitingCount().toString());
- }
- },
- [result],
- );
+ const handleQueueJoined = async (joiningDetails: {
+ queuename: string;
+ callerid: { id: string };
+ queuedcalls: string;
+ }): Promise => {
+ queueAggregator.queueJoined(joiningDetails);
+ setQueueCounter(queueAggregator.getCallWaitingCount().toString());
+ };
- const handleCallAbandon = useCallback(
- (queue: { queuename: string; queuedcallafterabandon: string }): void => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.queueAbandoned(queue);
- setQueueCounter(queueAggregator.getCallWaitingCount().toString());
- }
- },
- [result],
- );
+ return subscribeToNotifyUser(`${user._id}/callerjoined`, handleQueueJoined);
+ }, [result, subscribeToNotifyUser, user, voipEnabled]);
- const handleQueueJoined = useCallback(
- async (joiningDetails: { queuename: string; callerid: { id: string }; queuedcalls: string }): Promise => {
- if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
- return;
- }
- const queueAggregator = result.voipClient.getAggregator();
- if (queueAggregator) {
- queueAggregator.queueJoined(joiningDetails);
- setQueueCounter(queueAggregator.getCallWaitingCount().toString());
- }
- },
- [result],
- );
+ useEffect(() => {
+ if (!voipEnabled || !user) {
+ return;
+ }
- const handleCallHangup = useCallback(
- (_event: { roomId: string }) => {
- openWrapUpModal();
- },
- [openWrapUpModal],
- );
+ if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
+ return;
+ }
+
+ const queueAggregator = result.voipClient.getAggregator();
+ if (!queueAggregator) {
+ return;
+ }
+
+ const handleAgentConnected = (queue: { queuename: string; queuedcalls: string; waittimeinqueue: string }): void => {
+ queueAggregator.callPickedup(queue);
+ setQueueCounter(queueAggregator.getCallWaitingCount().toString());
+ };
+
+ return subscribeToNotifyUser(`${user._id}/agentconnected`, handleAgentConnected);
+ }, [result, subscribeToNotifyUser, user, voipEnabled]);
useEffect(() => {
- Notifications.onUser('callerjoined', handleQueueJoined);
- Notifications.onUser('agentcalled', handleAgentCalled);
- Notifications.onUser('agentconnected', handleAgentConnected);
- Notifications.onUser('queuememberadded', handleMemberAdded);
- Notifications.onUser('queuememberremoved', handleMemberRemoved);
- Notifications.onUser('callabandoned', handleCallAbandon);
- Notifications.onUser('call.callerhangup', handleCallHangup);
- Notifications.onUser('call.callerhangup', handleCallHangup);
- }, [
- handleQueueJoined,
- handleMemberAdded,
- handleMemberRemoved,
- handleCallAbandon,
- handleAgentConnected,
- handleCallHangup,
- handleAgentCalled,
- ]);
+ if (!voipEnabled || !user) {
+ return;
+ }
+
+ if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
+ return;
+ }
+
+ const queueAggregator = result.voipClient.getAggregator();
+ if (!queueAggregator) {
+ return;
+ }
+
+ const handleMemberAdded = (queue: { queuename: string; queuedcalls: string }): void => {
+ queueAggregator.memberAdded(queue);
+ setQueueCounter(queueAggregator.getCallWaitingCount().toString());
+ };
+
+ return subscribeToNotifyUser(`${user._id}/queuememberadded`, handleMemberAdded);
+ }, [result, subscribeToNotifyUser, user, voipEnabled]);
+
+ useEffect(() => {
+ if (!voipEnabled || !user) {
+ return;
+ }
+
+ if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
+ return;
+ }
+
+ const queueAggregator = result.voipClient.getAggregator();
+ if (!queueAggregator) {
+ return;
+ }
+
+ const handleMemberRemoved = (queue: { queuename: string; queuedcalls: string }): void => {
+ queueAggregator.memberRemoved(queue);
+ setQueueCounter(queueAggregator.getCallWaitingCount().toString());
+ };
+
+ return subscribeToNotifyUser(`${user._id}/queuememberremoved`, handleMemberRemoved);
+ }, [result, subscribeToNotifyUser, user, voipEnabled]);
+
+ useEffect(() => {
+ if (!voipEnabled || !user) {
+ return;
+ }
+
+ if (isUseVoipClientResultError(result) || isUseVoipClientResultLoading(result)) {
+ return;
+ }
+
+ const queueAggregator = result.voipClient.getAggregator();
+ if (!queueAggregator) {
+ return;
+ }
+
+ const handleCallAbandon = (queue: { queuename: string; queuedcallafterabandon: string }): void => {
+ queueAggregator.queueAbandoned(queue);
+ setQueueCounter(queueAggregator.getCallWaitingCount().toString());
+ };
+
+ return subscribeToNotifyUser(`${user._id}/callabandoned`, handleCallAbandon);
+ }, [result, subscribeToNotifyUser, user, voipEnabled]);
+
+ useEffect(() => {
+ if (!voipEnabled || !user) {
+ return;
+ }
+
+ const handleCallHangup = (_event: { roomId: string }): void => {
+ openWrapUpModal();
+ };
+
+ return subscribeToNotifyUser(`${user._id}/call.callerhangup`, handleCallHangup);
+ }, [openWrapUpModal, result, subscribeToNotifyUser, user, voipEnabled]);
useEffect(() => {
if (isUseVoipClientResultError(result)) {
@@ -169,7 +185,8 @@ export const CallProvider: FC = ({ children }) => {
if (isUseVoipClientResultLoading(result)) {
return;
}
- /**
+
+ /*
* This code may need a revisit when we handle callinqueue differently.
* Check clickup taks for more details
* https://app.clickup.com/t/22hy1k4
@@ -231,6 +248,7 @@ export const CallProvider: FC = ({ children }) => {
error: result.error,
};
}
+
if (isUseVoipClientResultLoading(result)) {
return {
enabled: true,
@@ -292,6 +310,7 @@ export const CallProvider: FC = ({ children }) => {
openWrapUpModal,
};
}, [queueCounter, voipEnabled, homeRoute, openWrapUpModal, result, roomInfo, user, visitorEndpoint, voipCloseRoomEndpoint, voipEndpoint]);
+
return (
{children}
diff --git a/client/providers/CallProvider/hooks/useVoipClient.ts b/client/providers/CallProvider/hooks/useVoipClient.ts
index 44118836441d..7c3dd350c6cb 100644
--- a/client/providers/CallProvider/hooks/useVoipClient.ts
+++ b/client/providers/CallProvider/hooks/useVoipClient.ts
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
import { IRegistrationInfo } from '../../../../definition/voip/IRegistrationInfo';
import { WorkflowTypes } from '../../../../definition/voip/WorkflowTypes';
import { useEndpoint } from '../../../contexts/ServerContext';
+import { useSetting } from '../../../contexts/SettingsContext';
import { useUser } from '../../../contexts/UserContext';
import { SimpleVoipUser } from '../../../lib/voip/SimpleVoipUser';
import { VoIPUser } from '../../../lib/voip/VoIPUser';
@@ -28,6 +29,7 @@ export const isUseVoipClientResultLoading = (result: UseVoipClientResult): resul
const isSignedResponse = (data: any): data is { result: string } => typeof data?.result === 'string';
export const useVoipClient = (): UseVoipClientResult => {
+ const voipEnabled = useSetting('VoIP_Enabled');
const registrationInfo = useEndpoint('GET', 'connector.extension.getRegistrationInfoByUserId');
const membership = useEndpoint('GET', 'voip/queues.getMembershipSubscription');
const user = useUser();
@@ -36,10 +38,11 @@ export const useVoipClient = (): UseVoipClientResult => {
const [result, setResult] = useSafely(useState({}));
useEffect(() => {
- if (!user || !user?._id) {
+ if (!user || !user?._id || !voipEnabled) {
setResult({});
return;
}
+
registrationInfo({ id: user._id }).then(
(data) => {
let parsedData: IRegistrationInfo;
@@ -79,7 +82,7 @@ export const useVoipClient = (): UseVoipClientResult => {
// client?.disconnect();
// TODO how to close the client? before creating a new one?
};
- }, [user, iceServers, registrationInfo, setResult, membership]);
+ }, [user, iceServers, registrationInfo, setResult, membership, voipEnabled]);
return result;
};
diff --git a/client/views/admin/rooms/EditRoom.js b/client/views/admin/rooms/EditRoom.js
index 80cd07267147..f12441449902 100644
--- a/client/views/admin/rooms/EditRoom.js
+++ b/client/views/admin/rooms/EditRoom.js
@@ -1,4 +1,4 @@
-import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, Icon, Callout, TextAreaInput } from '@rocket.chat/fuselage';
+import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, Icon, TextAreaInput } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useState, useMemo } from 'react';
@@ -8,11 +8,14 @@ import VerticalBar from '../../../components/VerticalBar';
import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor';
import { usePermission } from '../../../contexts/AuthorizationContext';
import { useSetModal } from '../../../contexts/ModalContext';
-import { useMethod } from '../../../contexts/ServerContext';
+import { useRoute } from '../../../contexts/RouterContext';
+import { useEndpoint, useMethod } from '../../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useEndpointActionExperimental } from '../../../hooks/useEndpointActionExperimental';
import { useForm } from '../../../hooks/useForm';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
+import DeleteTeamModal from '../../teams/contextualBar/info/Delete/DeleteTeamModal';
const getInitialValues = (room) => ({
roomName: room.t === 'd' ? room.usernames.join(' x ') : roomCoordinator.getRoomName(room.t, { type: room.t, ...room }),
@@ -28,13 +31,13 @@ const getInitialValues = (room) => ({
roomAvatar: undefined,
});
-function EditRoom({ room, onChange }) {
+function EditRoom({ room, onChange, onDelete }) {
const t = useTranslation();
- const [deleted, setDeleted] = useState(false);
+ const [deleting, setDeleting] = useState(false);
const setModal = useSetModal();
-
+ const dispatchToastMessage = useToastMessageDispatch();
const { values, handlers, hasUnsavedChanges, reset } = useForm(getInitialValues(room));
const [canViewName, canViewTopic, canViewAnnouncement, canViewArchived, canViewDescription, canViewType, canViewReadOnly] =
@@ -81,6 +84,8 @@ function EditRoom({ room, onChange }) {
const changeArchivation = archived !== !!room.archived;
+ const roomsRoute = useRoute('admin-rooms');
+
const canDelete = usePermission(`delete-${room.t}`);
const archiveSelector = room.archived ? 'unarchive' : 'archive';
@@ -115,18 +120,57 @@ function EditRoom({ room, onChange }) {
handleRoomType(roomType === 'p' ? 'c' : 'p');
});
- const deleteRoom = useMethod('eraseRoom');
+ const eraseRoom = useMethod('eraseRoom');
+ const deleteTeam = useEndpoint('POST', 'teams.delete');
const handleDelete = useMutableCallback(() => {
- const onCancel = () => setModal(undefined);
- const onConfirm = async () => {
- await deleteRoom(room._id);
- onCancel();
- setDeleted(true);
- };
+ if (room.teamMain) {
+ setModal(
+ {
+ const roomsToRemove = Array.isArray(deletedRooms) && deletedRooms.length > 0 ? deletedRooms : [];
+
+ try {
+ setDeleting(true);
+ setModal(null);
+ await deleteTeam({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) });
+ dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') });
+ roomsRoute.push({});
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ setDeleting(false);
+ } finally {
+ onDelete();
+ }
+ }}
+ onCancel={() => setModal(null)}
+ teamId={room.teamId}
+ />,
+ );
+
+ return;
+ }
setModal(
-
+ {
+ try {
+ setDeleting(true);
+ setModal(null);
+ await eraseRoom(room._id);
+ dispatchToastMessage({ type: 'success', message: t('Room_has_been_deleted') });
+ roomsRoute.push({});
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ setDeleting(false);
+ } finally {
+ onDelete();
+ }
+ }}
+ onCancel={() => setModal(null)}
+ confirmText={t('Yes_delete_it')}
+ >
{t('Delete_Room_Warning')}
,
);
@@ -134,7 +178,6 @@ function EditRoom({ room, onChange }) {
return (
e.preventDefault())}>
- {deleted && }
{room.t !== 'd' && (
@@ -143,7 +186,7 @@ function EditRoom({ room, onChange }) {
{t('Name')}
-
+
{room.t !== 'd' && (
@@ -158,7 +201,7 @@ function EditRoom({ room, onChange }) {
{t('Description')}
-
+
)}
@@ -166,7 +209,7 @@ function EditRoom({ room, onChange }) {
{t('Announcement')}
-
+
)}
@@ -174,7 +217,7 @@ function EditRoom({ room, onChange }) {
{t('Topic')}
-
+
)}
@@ -182,7 +225,7 @@ function EditRoom({ room, onChange }) {
{t('Private')}
-
+
{t('Just_invited_people_can_access_this_channel')}
@@ -192,7 +235,7 @@ function EditRoom({ room, onChange }) {
{t('Read_only')}
-
+
{t('Only_authorized_users_can_write_new_messages')}
@@ -203,7 +246,7 @@ function EditRoom({ room, onChange }) {
{t('Room_archivation_state_true')}
-
+
@@ -214,7 +257,7 @@ function EditRoom({ room, onChange }) {
{t('Default')}
-
+
@@ -222,7 +265,7 @@ function EditRoom({ room, onChange }) {
{t('Favorite')}
-
+
@@ -230,7 +273,7 @@ function EditRoom({ room, onChange }) {
{t('Featured')}
-
+
@@ -238,10 +281,10 @@ function EditRoom({ room, onChange }) {
-
@@ -250,7 +293,7 @@ function EditRoom({ room, onChange }) {
-
+
{t('Delete')}
diff --git a/client/views/admin/rooms/EditRoomContextBar.js b/client/views/admin/rooms/EditRoomContextBar.js
index c0aa0a12224e..3e0318e65cea 100644
--- a/client/views/admin/rooms/EditRoomContextBar.js
+++ b/client/views/admin/rooms/EditRoomContextBar.js
@@ -4,9 +4,9 @@ import NotAuthorizedPage from '../../../components/NotAuthorizedPage';
import { usePermission } from '../../../contexts/AuthorizationContext';
import EditRoomWithData from './EditRoomWithData';
-function EditRoomContextBar({ rid }) {
+function EditRoomContextBar({ rid, onReload }) {
const canViewRoomAdministration = usePermission('view-room-administration');
- return canViewRoomAdministration ? : ;
+ return canViewRoomAdministration ? : ;
}
export default EditRoomContextBar;
diff --git a/client/views/admin/rooms/EditRoomWithData.js b/client/views/admin/rooms/EditRoomWithData.js
index ee815fe1a27b..cedf9bbdbe43 100644
--- a/client/views/admin/rooms/EditRoomWithData.js
+++ b/client/views/admin/rooms/EditRoomWithData.js
@@ -5,7 +5,7 @@ import { AsyncStatePhase } from '../../../hooks/useAsyncState';
import { useEndpointData } from '../../../hooks/useEndpointData';
import EditRoom from './EditRoom';
-function EditRoomWithData({ rid }) {
+function EditRoomWithData({ rid, onReload }) {
const {
value: data = {},
phase: state,
@@ -33,7 +33,16 @@ function EditRoomWithData({ rid }) {
return error.message;
}
- return ;
+ const handleChange = () => {
+ reload();
+ onReload();
+ };
+
+ const handleDelete = () => {
+ onReload();
+ };
+
+ return ;
}
export default EditRoomWithData;
diff --git a/client/views/admin/rooms/RoomsPage.js b/client/views/admin/rooms/RoomsPage.js
index 82cde4e34dae..179417e8c842 100644
--- a/client/views/admin/rooms/RoomsPage.js
+++ b/client/views/admin/rooms/RoomsPage.js
@@ -1,12 +1,28 @@
-import React from 'react';
+import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
+import React, { useState, useMemo } from 'react';
import Page from '../../../components/Page';
import VerticalBar from '../../../components/VerticalBar';
import { useRouteParameter, useRoute } from '../../../contexts/RouterContext';
import { useTranslation } from '../../../contexts/TranslationContext';
+import { useEndpointData } from '../../../hooks/useEndpointData';
import EditRoomContextBar from './EditRoomContextBar';
import RoomsTable from './RoomsTable';
+export const DEFAULT_TYPES = ['d', 'p', 'c', 'teams'];
+
+const useQuery = ({ text, types, itemsPerPage, current }, [column, direction]) =>
+ useMemo(
+ () => ({
+ filter: text || '',
+ types,
+ sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }),
+ ...(itemsPerPage && { count: itemsPerPage }),
+ ...(current && { offset: current }),
+ }),
+ [text, types, itemsPerPage, current, column, direction],
+ );
+
export function RoomsPage() {
const t = useTranslation();
@@ -19,12 +35,27 @@ export function RoomsPage() {
roomsRoute.push({});
};
+ const [params, setParams] = useState({
+ text: '',
+ types: DEFAULT_TYPES,
+ current: 0,
+ itemsPerPage: 25,
+ });
+ const [sort, setSort] = useState(['name', 'asc']);
+
+ const debouncedParams = useDebouncedValue(params, 500);
+ const debouncedSort = useDebouncedValue(sort, 500);
+
+ const query = useQuery(debouncedParams, debouncedSort);
+
+ const endpointData = useEndpointData('rooms.adminRooms', query);
+
return (
-
+
{context && (
@@ -34,7 +65,7 @@ export function RoomsPage() {
-
+
)}
diff --git a/client/views/admin/rooms/RoomsTable.js b/client/views/admin/rooms/RoomsTable.js
index c9efd973f6d1..a75f0a2521db 100644
--- a/client/views/admin/rooms/RoomsTable.js
+++ b/client/views/admin/rooms/RoomsTable.js
@@ -1,20 +1,17 @@
import { Box, Table, Icon } from '@rocket.chat/fuselage';
-import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks';
-import React, { useMemo, useCallback, useState } from 'react';
+import { useMediaQuery } from '@rocket.chat/fuselage-hooks';
+import React, { useMemo, useCallback } from 'react';
import GenericTable from '../../../components/GenericTable';
import RoomAvatar from '../../../components/avatar/RoomAvatar';
import { useRoute } from '../../../contexts/RouterContext';
import { useTranslation } from '../../../contexts/TranslationContext';
-import { useEndpointData } from '../../../hooks/useEndpointData';
import { AsyncStatePhase } from '../../../lib/asyncState';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
import FilterByTypeAndText from './FilterByTypeAndText';
const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' };
-export const DEFAULT_TYPES = ['d', 'p', 'c', 'teams'];
-
export const roomTypeI18nMap = {
l: 'Omnichannel',
c: 'Channel',
@@ -30,18 +27,6 @@ const getRoomType = (room) => {
return roomTypeI18nMap[room.t];
};
-const useQuery = ({ text, types, itemsPerPage, current }, [column, direction]) =>
- useMemo(
- () => ({
- filter: text || '',
- types,
- sort: JSON.stringify({ [column]: direction === 'asc' ? 1 : -1 }),
- ...(itemsPerPage && { count: itemsPerPage }),
- ...(current && { offset: current }),
- }),
- [text, types, itemsPerPage, current, column, direction],
- );
-
const getRoomDisplayName = (room) => (room.t === 'd' ? room.usernames.join(' x ') : roomCoordinator.getRoomName(room.t, room));
const useDisplayData = (asyncState, sort) =>
@@ -66,29 +51,14 @@ const useDisplayData = (asyncState, sort) =>
return value.rooms;
}, [asyncState, sort]);
-function RoomsTable() {
+function RoomsTable({ endpointData, params, onChangeParams, sort, onChangeSort }) {
const t = useTranslation();
const mediaQuery = useMediaQuery('(min-width: 1024px)');
- const [params, setParams] = useState({
- text: '',
- types: DEFAULT_TYPES,
- current: 0,
- itemsPerPage: 25,
- });
- const [sort, setSort] = useState(['name', 'asc']);
-
const routeName = 'admin-rooms';
- const debouncedParams = useDebouncedValue(params, 500);
- const debouncedSort = useDebouncedValue(sort, 500);
-
- const query = useQuery(debouncedParams, debouncedSort);
-
- const asyncState = useEndpointData('rooms.adminRooms', query);
-
- const { value: data = {} } = asyncState;
+ const { value: data = {} } = endpointData;
const router = useRoute(routeName);
@@ -106,15 +76,15 @@ function RoomsTable() {
const [sortBy, sortDirection] = sort;
if (sortBy === id) {
- setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
+ onChangeSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
return;
}
- setSort([id, 'asc']);
+ onChangeSort([id, 'asc']);
},
- [sort],
+ [sort, onChangeSort],
);
- const displayData = useDisplayData(asyncState, sort);
+ const displayData = useDisplayData(endpointData, sort);
const header = useMemo(
() =>
@@ -218,7 +188,7 @@ function RoomsTable() {
renderRow={renderRow}
results={displayData}
total={data.total}
- setParams={setParams}
+ setParams={onChangeParams}
params={params}
renderFilter={({ onChange, ...props }) => }
/>
diff --git a/client/views/admin/settings/groups/voip/AssignAgentModal.tsx b/client/views/admin/settings/groups/voip/AssignAgentModal.tsx
index d36f01f51f92..a74425b2e266 100644
--- a/client/views/admin/settings/groups/voip/AssignAgentModal.tsx
+++ b/client/views/admin/settings/groups/voip/AssignAgentModal.tsx
@@ -2,8 +2,9 @@ import { Button, ButtonGroup, Modal, Select, Field, FieldGroup } from '@rocket.c
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { FC, useState, useMemo } from 'react';
-import AutoCompleteAgent from '../../../../../components/AutoCompleteAgent';
+import AutoCompleteAgentWithoutExtension from '../../../../../components/AutoCompleteAgentWithoutExtension';
import { useEndpoint } from '../../../../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../../../contexts/TranslationContext';
import { AsyncStatePhase } from '../../../../../hooks/useAsyncState';
import { useEndpointData } from '../../../../../hooks/useEndpointData';
@@ -16,6 +17,7 @@ type AssignAgentModalParams = {
const AssignAgentModal: FC = ({ existingExtension, closeModal, reload }) => {
const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
const [agent, setAgent] = useState('');
const [extension, setExtension] = useState(existingExtension || '');
const query = useMemo(() => ({ type: 'available' as const, userId: agent }), [agent]);
@@ -24,9 +26,9 @@ const AssignAgentModal: FC = ({ existingExtension, close
const handleAssignment = useMutableCallback(async () => {
try {
- await assignAgent({ userId: agent, extension });
+ await assignAgent({ username: agent, extension });
} catch (error) {
- console.log(error);
+ dispatchToastMessage({ type: 'error', message: error.message });
}
reload();
closeModal();
@@ -45,7 +47,7 @@ const AssignAgentModal: FC = ({ existingExtension, close
{t('Agent_Without_Extensions')}
-
+
diff --git a/client/views/login/AppleOauth/AppleOauthButton.tsx b/client/views/login/AppleOauth/AppleOauthButton.tsx
index 672a64e5857a..8c6800da891d 100644
--- a/client/views/login/AppleOauth/AppleOauthButton.tsx
+++ b/client/views/login/AppleOauth/AppleOauthButton.tsx
@@ -73,6 +73,10 @@ export const AppleOauthButton: FC = () => {
}, [scriptLoadedHandler]);
useLayoutEffect(() => {
+ if (!enabled) {
+ return;
+ }
+
const script = document.createElement('script');
script.src = 'https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js';
script.async = true;
@@ -81,7 +85,7 @@ export const AppleOauthButton: FC = () => {
return (): void => {
document.body.removeChild(script);
};
- }, []);
+ }, [enabled]);
if (!enabled) {
return null;
diff --git a/client/views/teams/CreateTeamModal/CreateTeamModal.tsx b/client/views/teams/CreateTeamModal/CreateTeamModal.tsx
index b231b7ec7cc5..8da626fe91cd 100644
--- a/client/views/teams/CreateTeamModal/CreateTeamModal.tsx
+++ b/client/views/teams/CreateTeamModal/CreateTeamModal.tsx
@@ -303,7 +303,7 @@ const CreateTeamModal: FC = ({ onClose }) => {
({t('optional')})
-
+
diff --git a/definition/rest/v1/voip.ts b/definition/rest/v1/voip.ts
index 89994a27ed93..4cf42fbf630f 100644
--- a/definition/rest/v1/voip.ts
+++ b/definition/rest/v1/voip.ts
@@ -37,6 +37,9 @@ export type VoipEndpoints = {
POST: (params: { userId: string; extension: string } | { username: string; extension: string }) => void;
DELETE: (params: { username: string }) => void;
};
+ 'omnichannel/agents/available': {
+ GET: (params: PaginatedRequest<{ text?: string; includeExtension?: string }>) => PaginatedResult<{ agents: ILivechatAgent[] }>;
+ };
'voip/events': {
POST: (params: { event: VoipClientEvents; rid: string; comment?: string }) => void;
};
diff --git a/package-lock.json b/package-lock.json
index e1edfcb89066..eadf333f1d12 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "Rocket.Chat",
- "version": "4.5.0",
+ "version": "4.5.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -5377,29 +5377,29 @@
}
},
"@rocket.chat/css-in-js": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.31.4.tgz",
- "integrity": "sha512-WH5faDA62GFBg3v/k1NFX8e9j1km5BNrBxiLXYCIDyf9Um11Mxlo+ssDuww67HNleOcWuRaVtCZGSMXA4r351A==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.31.5.tgz",
+ "integrity": "sha512-qtSJghYOrLonIdWbFl7hP9E/bUBQKpbQ2KnsP91MsbjCUwFbPdAFnl690yaxHViv1SXgxZsRRSLHKwjcqwBODw==",
"requires": {
"@emotion/hash": "^0.8.0",
- "@rocket.chat/css-supports": "^0.31.4",
- "@rocket.chat/memo": "^0.31.4",
- "@rocket.chat/stylis-logical-props-middleware": "^0.31.4",
+ "@rocket.chat/css-supports": "^0.31.5",
+ "@rocket.chat/memo": "^0.31.5",
+ "@rocket.chat/stylis-logical-props-middleware": "^0.31.5",
"stylis": "~4.0.13"
}
},
"@rocket.chat/css-supports": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/css-supports/-/css-supports-0.31.4.tgz",
- "integrity": "sha512-JmZtbydVlNhI+cp2LJjJMaaBJDpfOeQ/0TfnTkpCudvUYS5m5Y4v2PPPCn9VR5raBvagqfsd5tXfE5s/ksqDtg==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/css-supports/-/css-supports-0.31.5.tgz",
+ "integrity": "sha512-tGohngRSy/X3w/oSORAby5NQMUdE/QyxoFC5SiVtdnfOZhoVgyJIAINlYpg33aSP2SJkkr60pRm2FQx5WYtPjQ==",
"requires": {
- "@rocket.chat/memo": "^0.31.4"
+ "@rocket.chat/memo": "^0.31.5"
}
},
"@rocket.chat/emitter": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/emitter/-/emitter-0.31.4.tgz",
- "integrity": "sha512-gRuYcccStL5wOpWLoV3iCq2Lo5n9W14lJd7Qe1obNsezjYIiMHzJaQIWcbzZNDi0v3HiXxaN+xI6i00zuPnOoA=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/emitter/-/emitter-0.31.5.tgz",
+ "integrity": "sha512-KioMqqL53CksV5aPdxJyRl8QPxiU8GcO7pSicY7y0uU8gZklbnw/Z8J0hcpQkrDh7kkh24Lk8v8NIA56ub15Xw=="
},
"@rocket.chat/eslint-config": {
"version": "0.4.0",
@@ -5411,30 +5411,30 @@
}
},
"@rocket.chat/fuselage": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.31.4.tgz",
- "integrity": "sha512-R9v6/cMGKhWJnl763A41ifCf1aQSi86m+U7irMrCRT39Ha2QAt3cHB8puFZsn2CqOYHd+Ygw9M4nObm+8Oz+og==",
- "requires": {
- "@rocket.chat/css-in-js": "^0.31.4",
- "@rocket.chat/css-supports": "^0.31.4",
- "@rocket.chat/fuselage-tokens": "^0.31.4",
- "@rocket.chat/memo": "^0.31.4",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage/-/fuselage-0.31.5.tgz",
+ "integrity": "sha512-H3FtudLb42OvZo9QyW1SqlI3a9KygzdGtv5KKqlcgPpTn1fzcKcEh01WYd9Kz9FBVXYq3qhjGnnEkSUFB8DnUw==",
+ "requires": {
+ "@rocket.chat/css-in-js": "^0.31.5",
+ "@rocket.chat/css-supports": "^0.31.5",
+ "@rocket.chat/fuselage-tokens": "^0.31.5",
+ "@rocket.chat/memo": "^0.31.5",
"invariant": "^2.2.4",
"react-keyed-flatten-children": "^1.3.0"
}
},
"@rocket.chat/fuselage-hooks": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-hooks/-/fuselage-hooks-0.31.4.tgz",
- "integrity": "sha512-+6dJFU7MsojUffpifBCw0cvAt3IUp/dMUJa2ANBLsoBxLtCxYGAW8fFI7whilgnIzSXTiRrGgY1ePtJa3zS3ig==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-hooks/-/fuselage-hooks-0.31.5.tgz",
+ "integrity": "sha512-GsdEgAQycbzWo/eey0iodPMKuWClYjgBxz7BXISAXbFU8PzB4/HgVGGo/ER0SXO9pb/hDZf3pk9weW79tCai8Q==",
"requires": {
"@testing-library/user-event": "^13.5.0"
}
},
"@rocket.chat/fuselage-polyfills": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-polyfills/-/fuselage-polyfills-0.31.4.tgz",
- "integrity": "sha512-ZEtKQwMJyX+roUmsnozSUgWXPXgpngw5LNHHNchR6tgIKaqVyMgXdNoFeHcBlpucTpuw5UQsz3Q2KgYyO4d4Lw==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-polyfills/-/fuselage-polyfills-0.31.5.tgz",
+ "integrity": "sha512-7dkyKHSLHKEteQrhbzr1tRfoAocG6eDfSDmaKCxm6wrwxeDvO7HDHQ2OJZOOB+Y/zbCz44k+9QOBFRXILCgDxw==",
"requires": {
"@juggle/resize-observer": "^3.3.1",
"clipboard-polyfill": "^3.0.3",
@@ -5445,19 +5445,19 @@
}
},
"@rocket.chat/fuselage-tokens": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.31.4.tgz",
- "integrity": "sha512-tKU2DrpHylLyvT3QOb8WvPSUMvjP460miwtkjHST6Jl5Kza6iK2TbkkrjrXIoGX1GocFoIM/gIFOwA2EUJs0ew=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.31.5.tgz",
+ "integrity": "sha512-6vYPDM2ntjz/LR/PgIW1GtEDLtDJrUXFy6QEBhfVCUjO+1zSLZ2wVv5EtRawm5QYbsZN9ZC58tBEi/ZEBvPkjA=="
},
"@rocket.chat/fuselage-ui-kit": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.31.4.tgz",
- "integrity": "sha512-x530EyZyRsPJ+4/0b2cP/auHneXcIWyWvUAshBeyS5c1KXbFvEgSCbPfioxmMhKCEh75GlnAiy7Chmp03hBuhQ==",
- "requires": {
- "@rocket.chat/fuselage": "^0.31.4",
- "@rocket.chat/fuselage-hooks": "^0.31.4",
- "@rocket.chat/styled": "^0.31.4",
- "@rocket.chat/ui-kit": "^0.31.4",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.31.5.tgz",
+ "integrity": "sha512-JJx/4VlHbnPTflMLCORmFrg3haWO8cPdYSIbcVrkrESsRzGXIrPmg0XHcGS2WYdU+8BxceUyvobEXSQWjj9N4w==",
+ "requires": {
+ "@rocket.chat/fuselage": "^0.31.5",
+ "@rocket.chat/fuselage-hooks": "^0.31.5",
+ "@rocket.chat/styled": "^0.31.5",
+ "@rocket.chat/ui-kit": "^0.31.5",
"tslib": "^2.3.1"
},
"dependencies": {
@@ -5469,14 +5469,14 @@
}
},
"@rocket.chat/icons": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.31.4.tgz",
- "integrity": "sha512-1QvpZLd92/lyDHN7pXPtcZ0+oEEpXzyjTek1WJBDzhHFFdF14XKT2gtk/UMOG73F1HscTrYDKYnWv4jNsPN09w=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/icons/-/icons-0.31.5.tgz",
+ "integrity": "sha512-p67kcmnWDfV3ejRq890DmGFXWX77DfhKvBq/vC/GN5RnXAxubH7zsUqLSvPotCcvwO5pllm1CjhoVAmDrHc1FA=="
},
"@rocket.chat/livechat": {
- "version": "1.12.0",
- "resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.12.0.tgz",
- "integrity": "sha512-ePx2xLI3VRI23H+jgjsWfuO5o0x+pyPRPZWTE9pjH1MBxwLG5pteq8NVBX80WSQL+Lawu6k154B7lGhSsO5tQA==",
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.12.1.tgz",
+ "integrity": "sha512-Zn3WSthJtljibTome78jjOMpS2vCckCzqpFsJxQ/BBuXuXV3c04ow7YZDOqlxS8QK0Zy9/5Px+PhWW+MJ+5oXQ==",
"dev": true,
"requires": {
"@kossnocorp/desvg": "^0.2.0",
@@ -5542,12 +5542,12 @@
}
},
"@rocket.chat/logo": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/logo/-/logo-0.31.4.tgz",
- "integrity": "sha512-uJd/LP6pIOPF12L19VAGbYmoIcKi81S2HRRkjVsMDyzV91ZWOI2QNRnwqdnUNDCCSEH2HcCrXylT3MttXebDFw==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/logo/-/logo-0.31.5.tgz",
+ "integrity": "sha512-Jdl5fmbpBZDuqgzADfbh7ZW6FqBxOc6yQkddqBviycrtRnYDSahPSVl72O4Cck26AXb623LVe7fY7oTK6+evEg==",
"requires": {
- "@rocket.chat/fuselage-hooks": "^0.31.4",
- "@rocket.chat/styled": "^0.31.4",
+ "@rocket.chat/fuselage-hooks": "^0.31.5",
+ "@rocket.chat/styled": "^0.31.5",
"tslib": "^2.3.1"
},
"dependencies": {
@@ -5559,14 +5559,14 @@
}
},
"@rocket.chat/memo": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.31.4.tgz",
- "integrity": "sha512-Z67x7ymYumWA4WQV6R93OilLGD3a+esJKtx0pNNY2rwKH4VY3ecAleSef9XnZ2LDs2ehwHufFDJqTySVQhqe8g=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.31.5.tgz",
+ "integrity": "sha512-wzRGQO3qZ4NvMtiC3kJsK5HDIovtZu8D4r1juK47jirxEZXB3/8uxbOk3ICQIi2tGulyVTnojV/dfolBJeY+FA=="
},
"@rocket.chat/message-parser": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/message-parser/-/message-parser-0.31.4.tgz",
- "integrity": "sha512-6ljh/zz/lH4bM9M9f2Zk0r0j/c1VjG2Pc6L9yozVKZ2B0rSKCnG5XuL/gN9rPMmfX75dj9qdeiW+qKwSSpsh7A=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/message-parser/-/message-parser-0.31.5.tgz",
+ "integrity": "sha512-TVckPikRlPIrWqxF1OvL+YIf2Q1zgtIjDL5isWjSG4x3AVkguuT00Nm7xIAEBc/9wStyx5xHGgQaF3o0Q4AnsQ=="
},
"@rocket.chat/mp3-encoder": {
"version": "0.24.0",
@@ -5577,15 +5577,15 @@
}
},
"@rocket.chat/onboarding-ui": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/onboarding-ui/-/onboarding-ui-0.31.4.tgz",
- "integrity": "sha512-feruACZNuA6zw9ScJtTsARbvTEcsWTGSr7B4bvE15eIUxUqk4+w14KDlS3kx1LwmDknSnrylPuT7LEwMZnDHfg==",
- "requires": {
- "@rocket.chat/fuselage": "^0.31.4",
- "@rocket.chat/fuselage-hooks": "^0.31.4",
- "@rocket.chat/icons": "^0.31.4",
- "@rocket.chat/logo": "^0.31.4",
- "@rocket.chat/styled": "^0.31.4",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/onboarding-ui/-/onboarding-ui-0.31.5.tgz",
+ "integrity": "sha512-xbQzbq0TNijUbQC4ffR2nTrL60gt6/lsE5yzhLXfJmT5mPOf1kyM+fu+4Ork7VkD6jo3JB3cPH64dA3bIfOUUw==",
+ "requires": {
+ "@rocket.chat/fuselage": "^0.31.5",
+ "@rocket.chat/fuselage-hooks": "^0.31.5",
+ "@rocket.chat/icons": "^0.31.5",
+ "@rocket.chat/logo": "^0.31.5",
+ "@rocket.chat/styled": "^0.31.5",
"i18next": "~21.6.11",
"react-hook-form": "~7.27.0",
"react-i18next": "~11.15.4",
@@ -5593,9 +5593,9 @@
},
"dependencies": {
"i18next": {
- "version": "21.6.11",
- "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.11.tgz",
- "integrity": "sha512-tJ2+o0lVO+fhi8bPkCpBAeY1SgkqmQm5NzgPWCQssBrywJw98/o+Kombhty5nxQOpHtvMmsxcOopczUiH6bJxQ==",
+ "version": "21.6.12",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-21.6.12.tgz",
+ "integrity": "sha512-xlGTPdu2g5PZEUIE6TA1mQ9EIAAv9nMFONzgwAIrKL/KTmYYWufQNGgOmp5Og1PvgUji+6i1whz0rMdsz1qaKw==",
"requires": {
"@babel/runtime": "^7.12.0"
}
@@ -5655,9 +5655,9 @@
}
},
"@rocket.chat/string-helpers": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/string-helpers/-/string-helpers-0.31.4.tgz",
- "integrity": "sha512-tDphtvQ+kE1PJBYhynZrFJ2dDOwDkzcCgt1lToJ+C6d13uWcngWNhl0TpM71L65Q0FcBVP0hJE7jaD2gPDbwjQ==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/string-helpers/-/string-helpers-0.31.5.tgz",
+ "integrity": "sha512-MIQWeF69YF7/pazI59zZr1ZT04oHlg3oTyks2xCgh2IL3ZHAMhJcJ0yzl0GpigUbxhQ3OVI39fE7/5VanV6nhQ==",
"requires": {
"tslib": "^2.3.1"
},
@@ -5670,11 +5670,11 @@
}
},
"@rocket.chat/styled": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/styled/-/styled-0.31.4.tgz",
- "integrity": "sha512-tnuwRllCUAnjCXmIyyrMTlL3OvRmCNrL7qlixdrIOzSpDiF7K+pl6Iq2u1jfO3clUcQOVk39BjdH98E/Lx99WA==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/styled/-/styled-0.31.5.tgz",
+ "integrity": "sha512-ai3ixlmReEEyn/VsJuoEiAXdreHlpH8sYspqeBzfR3GPrIuNGePh5a7GLgdCYh2wrsdSdB0eSBzhf0UwJsT8Vg==",
"requires": {
- "@rocket.chat/css-in-js": "^0.31.4",
+ "@rocket.chat/css-in-js": "^0.31.5",
"tslib": "^2.3.1"
},
"dependencies": {
@@ -5686,11 +5686,11 @@
}
},
"@rocket.chat/stylis-logical-props-middleware": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/stylis-logical-props-middleware/-/stylis-logical-props-middleware-0.31.4.tgz",
- "integrity": "sha512-rjBqhfIueoHVez7CVz6AyEfW2t/vt3fW5+jSWvGGYR7ao1JHRpd1yLPYdsqM+gCr4pcQ6NhCW/cZsIO7cbaw7g==",
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/stylis-logical-props-middleware/-/stylis-logical-props-middleware-0.31.5.tgz",
+ "integrity": "sha512-GhpURYo9IY9fS5UYy+tJqfcqCPeTfXVIhSJUlyIf8b8RDSUTH5u4n0Gkvm0kXMMY6vo8UGsT+HrP6CZmbstbbg==",
"requires": {
- "@rocket.chat/css-supports": "^0.31.4",
+ "@rocket.chat/css-supports": "^0.31.5",
"tslib": "^2.3.1"
},
"dependencies": {
@@ -5702,9 +5702,9 @@
}
},
"@rocket.chat/ui-kit": {
- "version": "0.31.4",
- "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.31.4.tgz",
- "integrity": "sha512-wMoFyEbPh4bf5hZCY+vJ7qzGs5vL8zdZ8y2t5iyJ9WENcxqh/uylYAvfsEJhMx9D2GFuhg9gZreYGDTlxk1uFQ=="
+ "version": "0.31.5",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.31.5.tgz",
+ "integrity": "sha512-jmwFo//3th/VPu10qoMbL5CE8l6reaxrbo1JFoxGh+sfpBzeYmpmQ16hoLfydfRky9O+S4xBcvvZt+CIIKZ/zg=="
},
"@samverschueren/stream-to-observable": {
"version": "0.3.1",
@@ -22109,9 +22109,9 @@
"integrity": "sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA=="
},
"history": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
- "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
+ "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.7.6"
@@ -30352,9 +30352,9 @@
"integrity": "sha1-3F4yN2WYXd/cv9r8MUGpVprvdak="
},
"preact": {
- "version": "10.6.4",
- "resolved": "https://registry.npmjs.org/preact/-/preact-10.6.4.tgz",
- "integrity": "sha512-WyosM7pxGcndU8hY0OQlLd54tOU+qmG45QXj2dAYrL11HoyU/EzOSTlpJsirbBr1QW7lICxSsVJJmcmUglovHQ==",
+ "version": "10.6.6",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.6.6.tgz",
+ "integrity": "sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw==",
"dev": true
},
"preact-i18nline": {
@@ -31712,9 +31712,9 @@
"integrity": "sha512-Q2zaeQFXdVQ8l3hcywhltH+Nzj4vo50wMVujHDVN/1Xy9IOaSZJwYBXA2CYTpK6rq41fnXviw3jTLb04c7Gu9Q=="
},
"react-i18next": {
- "version": "11.15.4",
- "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.15.4.tgz",
- "integrity": "sha512-jKJNAcVcbPGK+yrTcXhLblgPY16n6NbpZZL3Mk8nswj1v3ayIiUBVDU09SgqnT+DluyQBS97hwSvPU5yVFG0yg==",
+ "version": "11.15.5",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.15.5.tgz",
+ "integrity": "sha512-vBWuVEQgrhZrGKpyv8FmJ7Zs5jRQWl794Tte7yzJ0okZqqi3jd6j2pLYNg441WcREsbIOvWdiDXbY7W6E93p1A==",
"requires": {
"@babel/runtime": "^7.14.5",
"html-escaper": "^2.0.2",
diff --git a/package.json b/package.json
index f5b05681b286..c762fe2b2737 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "Rocket.Chat",
"description": "The Ultimate Open Source WebChat Platform",
- "version": "4.5.0",
+ "version": "4.5.1",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
@@ -63,7 +63,7 @@
"@babel/preset-react": "^7.14.5",
"@babel/register": "^7.14.5",
"@rocket.chat/eslint-config": "^0.4.0",
- "@rocket.chat/livechat": "^1.12.0",
+ "@rocket.chat/livechat": "^1.12.1",
"@settlin/spacebars-loader": "^1.0.9",
"@storybook/addon-essentials": "^6.3.12",
"@storybook/addon-postcss": "^2.0.0",
@@ -178,21 +178,21 @@
"@nivo/line": "0.62.0",
"@nivo/pie": "0.73.0",
"@rocket.chat/apps-engine": "^1.31.0",
- "@rocket.chat/css-in-js": "~0.31.4",
- "@rocket.chat/emitter": "~0.31.4",
- "@rocket.chat/fuselage": "~0.31.4",
- "@rocket.chat/fuselage-hooks": "~0.31.4",
- "@rocket.chat/fuselage-polyfills": "~0.31.4",
- "@rocket.chat/fuselage-tokens": "~0.31.4",
- "@rocket.chat/fuselage-ui-kit": "~0.31.4",
- "@rocket.chat/icons": "~0.31.4",
- "@rocket.chat/logo": "~0.31.4",
- "@rocket.chat/memo": "~0.31.4",
- "@rocket.chat/message-parser": "~0.31.4",
+ "@rocket.chat/css-in-js": "^0.31.5",
+ "@rocket.chat/emitter": "^0.31.5",
+ "@rocket.chat/fuselage": "^0.31.5",
+ "@rocket.chat/fuselage-hooks": "^0.31.5",
+ "@rocket.chat/fuselage-polyfills": "^0.31.5",
+ "@rocket.chat/fuselage-tokens": "^0.31.5",
+ "@rocket.chat/fuselage-ui-kit": "^0.31.5",
+ "@rocket.chat/icons": "^0.31.5",
+ "@rocket.chat/logo": "^0.31.5",
+ "@rocket.chat/memo": "^0.31.5",
+ "@rocket.chat/message-parser": "^0.31.5",
"@rocket.chat/mp3-encoder": "^0.24.0",
- "@rocket.chat/onboarding-ui": "~0.31.4",
- "@rocket.chat/string-helpers": "~0.31.4",
- "@rocket.chat/ui-kit": "~0.31.4",
+ "@rocket.chat/onboarding-ui": "^0.31.5",
+ "@rocket.chat/string-helpers": "^0.31.5",
+ "@rocket.chat/ui-kit": "^0.31.5",
"@slack/client": "^4.12.0",
"@types/cookie": "^0.4.1",
"@types/lodash": "^4.14.177",
diff --git a/packages/rocketchat-i18n/i18n/ar.i18n.json b/packages/rocketchat-i18n/i18n/ar.i18n.json
index b588b77f8e4c..6dcc07d1bccd 100644
--- a/packages/rocketchat-i18n/i18n/ar.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ar.i18n.json
@@ -2890,7 +2890,11 @@
"Why_do_you_want_to_report_question_mark": "لماذا تريد الإبلاغ عن؟",
"will_be_able_to": "سوف يكون قادر على",
"Worldwide": "في جميع أنحاء العالم",
- "Would_you_like_to_return_the_inquiry": "هل ترغب بإعادة الطلب؟",
+ "Would_you_like_to_return_the_inquiry": "هل ترغب في إعادة الاستفسار؟",
+ "Would_you_like_to_return_the_queue": "هل ترغب في إعادة هذه الغرفة إلى قائمة الانتظار؟ سيتم الاحتفاظ بكل محفوظات المحادثات في الغرفة.",
+ "Would_you_like_to_place_chat_on_hold": "هل ترغب في وضع هذه الدردشة قيد الانتظار؟",
+ "Wrap_up_the_call": "إتمام المكالمة",
+ "Wrap_Up_Notes": "ملحوظات مختصرة",
"Yes": "نعم",
"Yes_archive_it": "نعم، قم بأرشفته!",
"Yes_clear_all": "نعم، حذف الكل!",
diff --git a/packages/rocketchat-i18n/i18n/de.i18n.json b/packages/rocketchat-i18n/i18n/de.i18n.json
index 4b2322574bfe..021a8daabea8 100644
--- a/packages/rocketchat-i18n/i18n/de.i18n.json
+++ b/packages/rocketchat-i18n/i18n/de.i18n.json
@@ -4110,7 +4110,10 @@
"will_be_able_to": "wird in der Lage sein,",
"Worldwide": "Weltweit",
"Would_you_like_to_return_the_inquiry": "Anfrage zurückgeben?",
- "Would_you_like_to_place_chat_on_hold": "Möchtest du diesen Chat in die Warteschleife legen?",
+ "Would_you_like_to_return_the_queue": "Möchten Sie diesen Raum zurück in die Warteschlange stellen? Der gesamte Gesprächsverlauf für den Raum wird aufbewahrt.",
+ "Would_you_like_to_place_chat_on_hold": "Möchten Sie diesen Chat in die Warteschleife stellen?",
+ "Wrap_up_the_call": "Gespräch abschließen",
+ "Wrap_Up_Notes": "Abschließende Notizen",
"Yes": "Ja",
"Yes_archive_it": "Ja, archivieren!",
"Yes_clear_all": "Ja, alles löschen!",
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index 09d371da645d..3f625aa1b06f 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -1490,6 +1490,7 @@
"Do_not_display_unread_counter": "Do not display any counter of this channel",
"Do_not_provide_this_code_to_anyone": "Do not provide this code to anyone.",
"Do_Nothing": "Do Nothing",
+ "Do_you_have_any_notes_for_this_conversation": "Do you have any notes for this conversation?",
"Do_you_want_to_accept": "Do you want to accept?",
"Do_you_want_to_change_to_s_question": "Do you want to change to %s?",
"Document_Domain": "Document Domain",
@@ -4799,8 +4800,8 @@
"Would_you_like_to_return_the_inquiry": "Would you like to return the inquiry?",
"Would_you_like_to_return_the_queue": "Would you like to move back this room to the queue? All conversation history will be kept on the room.",
"Would_you_like_to_place_chat_on_hold": "Would you like to place this chat On-Hold?",
- "Wrap_Up_the_Call": "Wrap Up the Call",
- "Wrap_Up_Notes": "Wrap Up Notes",
+ "Wrap_up_the_call": "Wrap-up the call",
+ "Wrap_Up_Notes": "Wrap-Up Notes",
"Yes": "Yes",
"Yes_archive_it": "Yes, archive it!",
"Yes_clear_all": "Yes, clear all!",
diff --git a/packages/rocketchat-i18n/i18n/fr.i18n.json b/packages/rocketchat-i18n/i18n/fr.i18n.json
index 40b8e85e026b..5a41e0db832e 100644
--- a/packages/rocketchat-i18n/i18n/fr.i18n.json
+++ b/packages/rocketchat-i18n/i18n/fr.i18n.json
@@ -4728,6 +4728,8 @@
"Would_you_like_to_return_the_inquiry": "Souhaitez-vous retourner la demande ?",
"Would_you_like_to_return_the_queue": "Souhaitez-vous replacer ce salon dans la file d'attente ? Tout l'historique des conversations sera conservé dans le salon.",
"Would_you_like_to_place_chat_on_hold": "Souhaitez-vous mettre ce chat en attente ?",
+ "Wrap_up_the_call": "Terminer l'appel",
+ "Wrap_Up_Notes": "Notes de conclusion",
"Yes": "Oui",
"Yes_archive_it": "Oui, archivez-le !",
"Yes_clear_all": "Oui, effacez tout !",
diff --git a/packages/rocketchat-i18n/i18n/ja.i18n.json b/packages/rocketchat-i18n/i18n/ja.i18n.json
index 674df759ead3..c146daa19e29 100644
--- a/packages/rocketchat-i18n/i18n/ja.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ja.i18n.json
@@ -4112,7 +4112,11 @@
"Will_be_available_here_after_saving": "保存後、こちらからご利用いただけます。",
"Without_priority": "優先権なし",
"Worldwide": "全世界",
- "Would_you_like_to_return_the_inquiry": "お問い合わせを返信しますか?",
+ "Would_you_like_to_return_the_inquiry": "問い合わせを返しますか?",
+ "Would_you_like_to_return_the_queue": "このルームをキューに戻しますか?すべての会話履歴がルームに保持されます。",
+ "Would_you_like_to_place_chat_on_hold": "このチャットを保留中にしますか?",
+ "Wrap_up_the_call": "通話の要約",
+ "Wrap_Up_Notes": "メモの要約",
"Yes": "はい",
"Yes_archive_it": "はい、アーカイブしてください!",
"Yes_clear_all": "はい、すべてクリアします!",
diff --git a/packages/rocketchat-i18n/i18n/nl.i18n.json b/packages/rocketchat-i18n/i18n/nl.i18n.json
index e090982dc78a..f6ed85b4ffdc 100644
--- a/packages/rocketchat-i18n/i18n/nl.i18n.json
+++ b/packages/rocketchat-i18n/i18n/nl.i18n.json
@@ -4728,6 +4728,8 @@
"Would_you_like_to_return_the_inquiry": "Wilt u de aanvraag retourneren?",
"Would_you_like_to_return_the_queue": "Wilt u deze kamer weer in de wachtrij plaatsen? Alle gespreksgeschiedenis wordt in de kamer bewaard.",
"Would_you_like_to_place_chat_on_hold": "Wilt u deze chat on-hold zetten?",
+ "Wrap_up_the_call": "Rond het gesprek af",
+ "Wrap_Up_Notes": "Afsluitende notities",
"Yes": "Ja",
"Yes_archive_it": "Ja, archiveer het!",
"Yes_clear_all": "Ja, alles wissen!",
diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
index 7026d1dfdfe0..733876540a32 100644
--- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
@@ -4706,6 +4706,8 @@
"Would_you_like_to_return_the_inquiry": "Gostaria de devolver o inquérito?",
"Would_you_like_to_return_the_queue": "Você gostaria de mover este chat de volta para a fila? Todo o histórico da conversa será mantido.",
"Would_you_like_to_place_chat_on_hold": "Gostaria de colocar essa conversa Em Espera?",
+ "Wrap_up_the_call": "Encerrar a chamada",
+ "Wrap_Up_Notes": "Notas de encerramento",
"Yes": "Sim",
"Yes_archive_it": "Sim, arquivar!",
"Yes_clear_all": "Sim, limpar tudo!",
diff --git a/server/lib/logger/getPino.ts b/server/lib/logger/getPino.ts
index 5c526b82abdf..30b3219e8182 100644
--- a/server/lib/logger/getPino.ts
+++ b/server/lib/logger/getPino.ts
@@ -21,7 +21,6 @@ const mainPino = pino({
subscription: 35,
startup: 51,
},
- name: '',
level: 'warn',
timestamp: pino.stdTimeFunctions.isoTime,
...(process.env.NODE_ENV !== 'production'
diff --git a/server/sdk/types/IOmnichannelVoipService.ts b/server/sdk/types/IOmnichannelVoipService.ts
index e41179e1b63b..ef4512223338 100644
--- a/server/sdk/types/IOmnichannelVoipService.ts
+++ b/server/sdk/types/IOmnichannelVoipService.ts
@@ -30,4 +30,11 @@ export interface IOmnichannelVoipService {
): Promise;
getExtensionListWithAgentData(): Promise;
findVoipRooms(filter: FindVoipRoomsParams): Promise>;
+ getAvailableAgents(
+ includeExtension?: string,
+ text?: string,
+ count?: number,
+ offset?: number,
+ sort?: Record,
+ ): Promise<{ agents: ILivechatAgent[]; total: number }>;
}
diff --git a/server/services/omnichannel-voip/service.ts b/server/services/omnichannel-voip/service.ts
index d298b3b2d325..3dfec93c94f5 100644
--- a/server/services/omnichannel-voip/service.ts
+++ b/server/services/omnichannel-voip/service.ts
@@ -458,4 +458,21 @@ export class OmnichannelVoipService extends ServiceClassInternal implements IOmn
this.logger.warn({ msg: 'Invalid room type or event type', type: room.t, event });
}
}
+
+ async getAvailableAgents(
+ includeExtension?: string,
+ text?: string,
+ count?: number,
+ offset?: number,
+ sort?: Record,
+ ): Promise<{ agents: ILivechatAgent[]; total: number }> {
+ const cursor = this.users.getAvailableAgentsIncludingExt(includeExtension, text, { count, skip: offset, sort });
+ const agents = await cursor.toArray();
+ const total = await cursor.count();
+
+ return {
+ agents,
+ total,
+ };
+ }
}
diff --git a/server/services/team/service.ts b/server/services/team/service.ts
index ef963152d987..5ed035d525fa 100644
--- a/server/services/team/service.ts
+++ b/server/services/team/service.ts
@@ -374,7 +374,9 @@ export class TeamService extends ServiceClassInternal implements ITeamService {
throw new Error('error-no-owner-channel');
}
- Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, user);
+ if (room.t === 'c') {
+ Messages.createUserAddRoomToTeamWithRoomIdAndUser(team.roomId, room.name, user);
+ }
room.teamId = teamId;
}
@@ -420,7 +422,9 @@ export class TeamService extends ServiceClassInternal implements ITeamService {
delete room.teamDefault;
this.RoomsModel.unsetTeamById(room._id);
- Messages.createUserRemoveRoomFromTeamWithRoomIdAndUser(team.roomId, room.name, user);
+ if (room.t === 'c') {
+ Messages.createUserRemoveRoomFromTeamWithRoomIdAndUser(team.roomId, room.name, user);
+ }
return {
...room,