diff --git a/.changeset/brown-crabs-chew.md b/.changeset/brown-crabs-chew.md new file mode 100644 index 0000000000000..3291f18bf2254 --- /dev/null +++ b/.changeset/brown-crabs-chew.md @@ -0,0 +1,13 @@ +--- +'@rocket.chat/uikit-playground': patch +'@rocket.chat/fuselage-ui-kit': patch +'@rocket.chat/ui-theming': patch +'@rocket.chat/ui-video-conf': patch +'@rocket.chat/ui-composer': patch +'@rocket.chat/gazzodown': patch +'@rocket.chat/ui-avatar': patch +'@rocket.chat/ui-client': patch +'@rocket.chat/meteor': patch +--- + +Bumped @rocket.chat/fuselage that fixes the Menu onPointerUp event behavior diff --git a/.changeset/bump-patch-1722087664914.md b/.changeset/bump-patch-1722087664914.md new file mode 100644 index 0000000000000..e1eaa7980afb1 --- /dev/null +++ b/.changeset/bump-patch-1722087664914.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1722559871139.md b/.changeset/bump-patch-1722559871139.md new file mode 100644 index 0000000000000..e1eaa7980afb1 --- /dev/null +++ b/.changeset/bump-patch-1722559871139.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/chatty-hounds-hammer.md b/.changeset/chatty-hounds-hammer.md new file mode 100644 index 0000000000000..1a2d3a7de559c --- /dev/null +++ b/.changeset/chatty-hounds-hammer.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/fuselage-ui-kit": patch +--- + +Fix validations from "UiKit" modal component diff --git a/.changeset/chilled-yaks-beg.md b/.changeset/chilled-yaks-beg.md new file mode 100644 index 0000000000000..670fa24887b7b --- /dev/null +++ b/.changeset/chilled-yaks-beg.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue in Marketplace that caused a subscription app to show incorrect modals when subscribing diff --git a/.changeset/cuddly-brooms-approve.md b/.changeset/cuddly-brooms-approve.md new file mode 100644 index 0000000000000..24905bb91c625 --- /dev/null +++ b/.changeset/cuddly-brooms-approve.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/i18n": minor +--- + +Allows admins to customize the `Subject` field of Omnichannel email transcripts via setting. By passing a value to the setting `Custom email subject for transcript`, system will use it as the `Subject` field, unless a custom subject is passed when requesting a transcript. If there's no custom subject and setting value is empty, the current default value will be used diff --git a/.changeset/dry-pumas-draw.md b/.changeset/dry-pumas-draw.md new file mode 100644 index 0000000000000..b66ca5157cd58 --- /dev/null +++ b/.changeset/dry-pumas-draw.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/livechat": patch +--- + +Fixed an issue that caused the widget to set the wrong department when using the setDepartment Livechat api endpoint in conjunction with a Livechat Trigger diff --git a/.changeset/empty-readers-teach.md b/.changeset/empty-readers-teach.md new file mode 100644 index 0000000000000..b4bd075ef654c --- /dev/null +++ b/.changeset/empty-readers-teach.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/tools": patch +"@rocket.chat/account-service": patch +--- + +Fixed an inconsistent evaluation of the `Accounts_LoginExpiration` setting over the codebase. In some places, it was being used as milliseconds while in others as days. Invalid values produced different results. A helper function was created to centralize the setting validation and the proper value being returned to avoid edge cases. +Negative values may be saved on the settings UI panel but the code will interpret any negative, NaN or 0 value to the default expiration which is 90 days. diff --git a/.changeset/fifty-mails-admire.md b/.changeset/fifty-mails-admire.md deleted file mode 100644 index b87fd11d47ee6..0000000000000 --- a/.changeset/fifty-mails-admire.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@rocket.chat/web-ui-registration': patch -"@rocket.chat/meteor": minor ---- - -Login services button was not respecting the button color and text color settings. Implemented a fix to respect these settings and change the button colors accordingly. - -Added a warning on all settings which allow admins to change OAuth button colors, so that they can be alerted about WCAG (Web Content Accessibility Guidelines) compliance. diff --git a/.changeset/funny-wolves-tie.md b/.changeset/funny-wolves-tie.md new file mode 100644 index 0000000000000..e2364ccb05e50 --- /dev/null +++ b/.changeset/funny-wolves-tie.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed issue where bad word filtering was not working in the UI for messages diff --git a/.changeset/gentle-news-wonder.md b/.changeset/gentle-news-wonder.md new file mode 100644 index 0000000000000..dd3218003d7ac --- /dev/null +++ b/.changeset/gentle-news-wonder.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Added a new 'Deactivated' tab to the users page, this tab lists users who have logged in for the first time but have been deactivated for any reason. Also added the UI code for the Active tab; diff --git a/.changeset/happy-peaches-nail.md b/.changeset/happy-peaches-nail.md new file mode 100644 index 0000000000000..2dfb2151ced05 --- /dev/null +++ b/.changeset/happy-peaches-nail.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue with livechat agents not being able to leave omnichannel rooms if joining after a room has been closed by the visitor (due to race conditions) diff --git a/.changeset/hungry-wombats-act.md b/.changeset/hungry-wombats-act.md new file mode 100644 index 0000000000000..4e50b172e17e7 --- /dev/null +++ b/.changeset/hungry-wombats-act.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue where non-encrypted attachments were not being downloaded diff --git a/.changeset/large-vans-attack.md b/.changeset/large-vans-attack.md new file mode 100644 index 0000000000000..c1008b2ca06ff --- /dev/null +++ b/.changeset/large-vans-attack.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed the contextual bar closing when editing thread messages instead of cancelling the message edit diff --git a/.changeset/lemon-steaks-provide.md b/.changeset/lemon-steaks-provide.md new file mode 100644 index 0000000000000..29f0289419a0c --- /dev/null +++ b/.changeset/lemon-steaks-provide.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/i18n': minor +'@rocket.chat/meteor': minor +--- + +Added an accordion for advanced settings on Create teams and channels diff --git a/.changeset/lucky-countries-look.md b/.changeset/lucky-countries-look.md new file mode 100644 index 0000000000000..79deda53edfcb --- /dev/null +++ b/.changeset/lucky-countries-look.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed the disappearance of some settings after navigation under network latency. diff --git a/.changeset/many-tables-love.md b/.changeset/many-tables-love.md new file mode 100644 index 0000000000000..8f37283c6a967 --- /dev/null +++ b/.changeset/many-tables-love.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/model-typings": minor +--- + +Fixed Livechat rooms being displayed in the Engagement Dashboard's "Channels" tab diff --git a/.changeset/cuddly-ravens-swim.md b/.changeset/mean-hairs-move.md similarity index 84% rename from .changeset/cuddly-ravens-swim.md rename to .changeset/mean-hairs-move.md index 5774ef48202d0..c92293d6ae953 100644 --- a/.changeset/cuddly-ravens-swim.md +++ b/.changeset/mean-hairs-move.md @@ -1,5 +1,5 @@ --- -"@rocket.chat/meteor": patch +'@rocket.chat/meteor': minor --- Fixed an issue where adding `OVERWRITE_SETTING_` for any setting wasn't immediately taking effect sometimes, and needed a server restart to reflect. diff --git a/.changeset/new-balloons-speak.md b/.changeset/new-balloons-speak.md new file mode 100644 index 0000000000000..7d4e7cd3a57e9 --- /dev/null +++ b/.changeset/new-balloons-speak.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed web client crashing on Firefox private window. Firefox disables access to service workers inside private windows. Rocket.Chat needs service workers to process E2EE encrypted files on rooms. These types of files won't be available inside private windows, but the rest of E2EE encrypted features should work normally diff --git a/.changeset/new-scissors-love.md b/.changeset/new-scissors-love.md new file mode 100644 index 0000000000000..fb962407b353e --- /dev/null +++ b/.changeset/new-scissors-love.md @@ -0,0 +1,12 @@ +--- +'@rocket.chat/omnichannel-services': minor +'@rocket.chat/pdf-worker': minor +'@rocket.chat/core-services': minor +'@rocket.chat/model-typings': minor +'@rocket.chat/i18n': minor +'@rocket.chat/meteor': minor +--- + +Added system messages support for Omnichannel PDF transcripts and email transcripts. Currently these transcripts don't render system messages and is shown as an empty message in PDF/email. This PR adds this support for all valid livechat system messages. + +Also added a new setting under transcripts, to toggle the inclusion of system messages in email and PDF transcripts. diff --git a/.changeset/nice-laws-eat.md b/.changeset/nice-laws-eat.md new file mode 100644 index 0000000000000..e99e4f219ef9b --- /dev/null +++ b/.changeset/nice-laws-eat.md @@ -0,0 +1,15 @@ +--- +'rocketchat-services': minor +'@rocket.chat/core-services': minor +'@rocket.chat/model-typings': minor +'@rocket.chat/ui-video-conf': minor +'@rocket.chat/core-typings': minor +'@rocket.chat/ui-contexts': minor +'@rocket.chat/models': minor +'@rocket.chat/ui-kit': minor +'@rocket.chat/i18n': minor +'@rocket.chat/meteor': minor +--- + +New Feature: Video Conference Persistent Chat. +This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. \ No newline at end of file diff --git a/.changeset/perfect-coins-camp.md b/.changeset/perfect-coins-camp.md new file mode 100644 index 0000000000000..4dbddf965742d --- /dev/null +++ b/.changeset/perfect-coins-camp.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed an issue in the "Create discussion" form, that would have the "Create" action button disabled even though the form is prefilled when opening it from the message action diff --git a/.changeset/polite-foxes-repair.md b/.changeset/polite-foxes-repair.md new file mode 100644 index 0000000000000..2f524c7e5f105 --- /dev/null +++ b/.changeset/polite-foxes-repair.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Added a method to the Apps-Engine that allows apps to read multiple messages from a room diff --git a/.changeset/popular-bottles-visit.md b/.changeset/popular-bottles-visit.md new file mode 100644 index 0000000000000..9e44e9dd71445 --- /dev/null +++ b/.changeset/popular-bottles-visit.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed an issue that caused UI to show an error when the call to get the Business Hour type from settings returned `undefined`. diff --git a/.changeset/popular-trees-lay.md b/.changeset/popular-trees-lay.md new file mode 100644 index 0000000000000..f38ef1f923670 --- /dev/null +++ b/.changeset/popular-trees-lay.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Removed 'Hide' option in the room menu for Omnichannel conversations. diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000000..38a7fe160e48d --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,114 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "@rocket.chat/meteor": "6.11.0-develop", + "rocketchat-services": "1.2.1", + "@rocket.chat/account-service": "0.4.1", + "@rocket.chat/authorization-service": "0.4.1", + "@rocket.chat/ddp-streamer": "0.3.1", + "@rocket.chat/omnichannel-transcript": "0.4.1", + "@rocket.chat/presence-service": "0.4.1", + "@rocket.chat/queue-worker": "0.4.1", + "@rocket.chat/stream-hub-service": "0.4.1", + "@rocket.chat/api-client": "0.2.1", + "@rocket.chat/ddp-client": "0.3.1", + "@rocket.chat/license": "0.2.1", + "@rocket.chat/omnichannel-services": "0.2.1", + "@rocket.chat/pdf-worker": "0.1.1", + "@rocket.chat/presence": "0.2.1", + "@rocket.chat/ui-theming": "0.2.0", + "@rocket.chat/account-utils": "0.0.2", + "@rocket.chat/agenda": "0.1.0", + "@rocket.chat/apps": "0.1.1", + "@rocket.chat/base64": "1.0.13", + "@rocket.chat/cas-validate": "0.0.2", + "@rocket.chat/core-services": "0.4.1", + "@rocket.chat/core-typings": "6.11.0-develop", + "@rocket.chat/cron": "0.1.1", + "@rocket.chat/eslint-config": "0.7.0", + "@rocket.chat/favicon": "0.0.2", + "@rocket.chat/fuselage-ui-kit": "8.0.1", + "@rocket.chat/gazzodown": "8.0.1", + "@rocket.chat/i18n": "0.5.0", + "@rocket.chat/instance-status": "0.1.1", + "@rocket.chat/jwt": "0.1.1", + "@rocket.chat/livechat": "1.18.1", + "@rocket.chat/log-format": "0.0.2", + "@rocket.chat/logger": "0.0.2", + "@rocket.chat/message-parser": "0.31.29", + "@rocket.chat/mock-providers": "0.1.0", + "@rocket.chat/model-typings": "0.5.1", + "@rocket.chat/models": "0.1.1", + "@rocket.chat/poplib": "0.0.2", + "@rocket.chat/password-policies": "0.0.2", + "@rocket.chat/patch-injection": "0.0.1", + "@rocket.chat/peggy-loader": "0.31.25", + "@rocket.chat/random": "1.2.2", + "@rocket.chat/release-action": "2.2.3", + "@rocket.chat/release-changelog": "0.1.0", + "@rocket.chat/rest-typings": "6.11.0-develop", + "@rocket.chat/server-cloud-communication": "0.0.2", + "@rocket.chat/server-fetch": "0.0.3", + "@rocket.chat/sha256": "1.0.10", + "@rocket.chat/tools": "0.2.1", + "@rocket.chat/ui-avatar": "4.0.1", + "@rocket.chat/ui-client": "8.0.1", + "@rocket.chat/ui-composer": "0.2.0", + "@rocket.chat/ui-contexts": "8.0.1", + "@rocket.chat/ui-kit": "0.35.0", + "@rocket.chat/ui-video-conf": "8.0.1", + "@rocket.chat/uikit-playground": "0.3.1", + "@rocket.chat/web-ui-registration": "8.0.1" + }, + "changesets": [ + "afraid-guests-jog", + "bump-patch-1722087664914", + "bump-patch-1722559871139", + "chatty-hounds-hammer", + "chilled-yaks-beg", + "chilly-papayas-march", + "cuddly-brooms-approve", + "dry-pumas-draw", + "empty-readers-teach", + "fast-buttons-shake", + "funny-snails-promise", + "funny-wolves-tie", + "grumpy-worms-appear", + "happy-peaches-nail", + "hip-queens-taste", + "hungry-wombats-act", + "large-vans-attack", + "lucky-beds-glow", + "lucky-countries-look", + "many-tables-love", + "mean-hairs-move", + "nervous-rockets-impress", + "new-balloons-speak", + "new-scissors-love", + "nice-laws-eat", + "perfect-coins-camp", + "polite-foxes-repair", + "popular-trees-lay", + "proud-waves-bathe", + "quick-ducks-live", + "rare-penguins-hope", + "red-numbers-happen", + "red-vans-shave", + "rich-carpets-brush", + "rotten-eggs-end", + "selfish-emus-sing", + "shaggy-hats-raise", + "sixty-nails-clean", + "smooth-lobsters-flash", + "soft-donkeys-thank", + "sour-forks-breathe", + "thin-windows-reply", + "violet-brooms-press", + "weak-insects-sort", + "weak-pets-talk", + "weak-taxis-design", + "weak-tigers-suffer", + "witty-bats-develop" + ] +} diff --git a/.changeset/proud-waves-bathe.md b/.changeset/proud-waves-bathe.md new file mode 100644 index 0000000000000..556fa3af80e12 --- /dev/null +++ b/.changeset/proud-waves-bathe.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/model-typings": minor +--- + +Improved Engagement Dashboard's "Channels" tab performance by not returning rooms that had no activity in the analyzed period diff --git a/.changeset/quick-ducks-live.md b/.changeset/quick-ducks-live.md new file mode 100644 index 0000000000000..ad628c13d0874 --- /dev/null +++ b/.changeset/quick-ducks-live.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed LDAP rooms, teams and roles syncs not being triggered on login even when the "Update User Data on Login" setting is enabled diff --git a/.changeset/red-numbers-happen.md b/.changeset/red-numbers-happen.md new file mode 100644 index 0000000000000..61cb0d2b7586e --- /dev/null +++ b/.changeset/red-numbers-happen.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed "Copy link" message action enabled in Starred and Pinned list for End to End Encrypted channels, this action is disabled now diff --git a/.changeset/red-vans-shave.md b/.changeset/red-vans-shave.md new file mode 100644 index 0000000000000..ddf76535087e0 --- /dev/null +++ b/.changeset/red-vans-shave.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed issue that caused unintentional clicks when scrolling the channels sidebar on safari/chrome in iOS diff --git a/.changeset/rich-carpets-brush.md b/.changeset/rich-carpets-brush.md new file mode 100644 index 0000000000000..16741e31e54ad --- /dev/null +++ b/.changeset/rich-carpets-brush.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed some anomalies related to disabled E2EE rooms. Earlier there are some weird issues with disabled E2EE rooms, this PR fixes these anomalies. diff --git a/.changeset/rotten-eggs-end.md b/.changeset/rotten-eggs-end.md new file mode 100644 index 0000000000000..7d0ad6ee5047e --- /dev/null +++ b/.changeset/rotten-eggs-end.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/i18n": patch +"@rocket.chat/ui-client": patch +--- + +Implemented a new tab to the users page called 'Active', this tab lists all users who have logged in for the first time and are active. diff --git a/.changeset/rude-dogs-burn.md b/.changeset/rude-dogs-burn.md new file mode 100644 index 0000000000000..e81f007820832 --- /dev/null +++ b/.changeset/rude-dogs-burn.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed a behavior when updating messages that prevented the `customFields` prop from being updated if there were no changes to the `msg` property. Now, `customFields` will be always updated on message update even if `msg` doesn't change diff --git a/.changeset/shaggy-hats-raise.md b/.changeset/shaggy-hats-raise.md new file mode 100644 index 0000000000000..40ee9f8fbb55a --- /dev/null +++ b/.changeset/shaggy-hats-raise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Added a new setting `Livechat_transcript_send_always` that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. diff --git a/.changeset/sixty-nails-clean.md b/.changeset/sixty-nails-clean.md new file mode 100644 index 0000000000000..7d13e02f0bd3f --- /dev/null +++ b/.changeset/sixty-nails-clean.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed an issue that prevented the option to start a discussion from being shown on the message actions diff --git a/.changeset/smooth-lobsters-flash.md b/.changeset/smooth-lobsters-flash.md new file mode 100644 index 0000000000000..541d5069ee9c2 --- /dev/null +++ b/.changeset/smooth-lobsters-flash.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fix show correct user roles after updating user roles on admin edit user panel. diff --git a/.changeset/soft-donkeys-thank.md b/.changeset/soft-donkeys-thank.md new file mode 100644 index 0000000000000..7273ddcffca48 --- /dev/null +++ b/.changeset/soft-donkeys-thank.md @@ -0,0 +1,8 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/mock-providers": patch +"@rocket.chat/ui-contexts": patch +"@rocket.chat/web-ui-registration": patch +--- + +Fixed an issue with blocked login when dismissed 2FA modal by clicking outside of it or pressing the escape key diff --git a/.changeset/strong-swans-double.md b/.changeset/strong-swans-double.md new file mode 100644 index 0000000000000..db521aeeef0f1 --- /dev/null +++ b/.changeset/strong-swans-double.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/uikit-playground': minor +'@rocket.chat/meteor': minor +--- + +Upgrades fuselage-toastbar version in order to add pause on hover functionality diff --git a/.changeset/swift-maps-tickle.md b/.changeset/swift-maps-tickle.md new file mode 100644 index 0000000000000..076ead1cea4cc --- /dev/null +++ b/.changeset/swift-maps-tickle.md @@ -0,0 +1,9 @@ +--- +'@rocket.chat/core-services': minor +'@rocket.chat/model-typings': minor +'@rocket.chat/core-typings': minor +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +Added `sidepanel` field to `teams.create` and `rooms.saveRoomSettings` endpoints diff --git a/.changeset/thin-windows-reply.md b/.changeset/thin-windows-reply.md new file mode 100644 index 0000000000000..1a32e1ddebfb2 --- /dev/null +++ b/.changeset/thin-windows-reply.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes an issue not displaying all groups in settings list diff --git a/.changeset/thirty-dryers-help.md b/.changeset/thirty-dryers-help.md new file mode 100644 index 0000000000000..6d9a7dc205ae6 --- /dev/null +++ b/.changeset/thirty-dryers-help.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/livechat": patch +--- + +Fixed issue where `after-registration-triggers` would show up in a page when the user was not yet registered diff --git a/.changeset/violet-brooms-press.md b/.changeset/violet-brooms-press.md new file mode 100644 index 0000000000000..632026d6fe2e1 --- /dev/null +++ b/.changeset/violet-brooms-press.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) diff --git a/.changeset/weak-insects-sort.md b/.changeset/weak-insects-sort.md new file mode 100644 index 0000000000000..cbbe7c4aa08c6 --- /dev/null +++ b/.changeset/weak-insects-sort.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Improving UX by change the position of room info actions buttons and menu order to avoid missclick in destructive actions. diff --git a/.changeset/weak-pets-talk.md b/.changeset/weak-pets-talk.md new file mode 100644 index 0000000000000..abaa9c683d65d --- /dev/null +++ b/.changeset/weak-pets-talk.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/omnichannel-services': patch +'@rocket.chat/core-services': patch +'@rocket.chat/meteor': patch +--- + +Reduced time on generation of PDF transcripts. Earlier Rocket.Chat was fetching the required translations everytime a PDF transcript was requested, this process was async and was being unnecessarily being performed on every pdf transcript request. This PR improves this and now the translations are loaded at the start and kept in memory to process further pdf transcripts requests. This reduces the time of asynchronously fetching translations again and again. diff --git a/.changeset/weak-taxis-design.md b/.changeset/weak-taxis-design.md new file mode 100644 index 0000000000000..a2d435495cd71 --- /dev/null +++ b/.changeset/weak-taxis-design.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Added handling of attachments in Omnichannel email transcripts. Earlier attachments were being skipped and were being shown as empty space, now it should render the image attachments and should show relevant error message for unsupported attachments. diff --git a/.changeset/weak-tigers-suffer.md b/.changeset/weak-tigers-suffer.md new file mode 100644 index 0000000000000..91748a43c6771 --- /dev/null +++ b/.changeset/weak-tigers-suffer.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/model-typings": minor +"@rocket.chat/rest-typings": minor +--- + +Added the ability to filter chats by `queued` on the Current Chats Omnichannel page diff --git a/.changeset/witty-bats-develop.md b/.changeset/witty-bats-develop.md new file mode 100644 index 0000000000000..42c9409d9ef37 --- /dev/null +++ b/.changeset/witty-bats-develop.md @@ -0,0 +1,13 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/apps": patch +"@rocket.chat/core-services": patch +"@rocket.chat/core-typings": patch +"@rocket.chat/fuselage-ui-kit": patch +"@rocket.chat/rest-typings": patch +"@rocket.chat/ddp-streamer": patch +"@rocket.chat/presence": patch +"rocketchat-services": patch +--- + +Added the `user` param to apps-engine update method call, allowing apps' new `onUpdate` hook to know who triggered the update. diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml index 364957ecdf010..75673c15bfd67 100644 --- a/.github/actions/build-docker/action.yml +++ b/.github/actions/build-docker/action.yml @@ -31,7 +31,7 @@ runs: password: ${{ inputs.CR_PAT }} - name: Restore build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build path: /tmp/build @@ -43,7 +43,7 @@ runs: tar xzf Rocket.Chat.tar.gz rm Rocket.Chat.tar.gz - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Setup NodeJS uses: ./.github/actions/setup-node diff --git a/.github/actions/meteor-build/action.yml b/.github/actions/meteor-build/action.yml index d261000ceb872..87c0c2d4a6529 100644 --- a/.github/actions/meteor-build/action.yml +++ b/.github/actions/meteor-build/action.yml @@ -91,7 +91,7 @@ runs: meteor node -v git version - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Translation check shell: bash @@ -124,7 +124,7 @@ runs: tar czf /tmp/Rocket.Chat.tar.gz bundle - name: Store build - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build path: /tmp/Rocket.Chat.tar.gz diff --git a/.github/workflows/ci-code-check.yml b/.github/workflows/ci-code-check.yml index 75deb399d2f2a..fd214bc39488d 100644 --- a/.github/workflows/ci-code-check.yml +++ b/.github/workflows/ci-code-check.yml @@ -42,7 +42,7 @@ jobs: # docker rmi $(docker image ls -aq) # df -h - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Cache TypeCheck uses: actions/cache@v3 diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 920aea0aa3087..c700512ec1efe 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -99,6 +99,15 @@ jobs: job_summary: true comment_on_pr: false + - name: Setup kernel limits + run: | + sudo sysctl -w net.ipv4.ip_local_port_range="500 65535" + sudo sysctl -w net.ipv4.tcp_mem="383865 511820 2303190" + + echo fs.file-max=20000500 | sudo tee -a /etc/sysctl.conf + echo fs.nr_open=20000500 | sudo tee -a /etc/sysctl.conf + sudo sysctl -p + - name: Login to GitHub Container Registry if: (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') uses: docker/login-action@v2 @@ -124,13 +133,13 @@ jobs: # if we are testing a PR from a fork, we need to build the docker image at this point - uses: ./.github/actions/build-docker - if: github.event.pull_request.head.repo.full_name != github.repository + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository with: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} node-version: ${{ inputs.node-version }} - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Start httpbin container and wait for it to be ready if: inputs.type == 'api' @@ -178,6 +187,12 @@ jobs: run: | docker compose -f docker-compose-ci.yml up -d + - name: Clean up temporary files + # remove all folders inside /tmp except /tmp/coverage + run: | + cd /tmp + sudo find . -mindepth 1 -maxdepth 1 -type d | grep -v './coverage' | sudo xargs rm -rf + - name: Cache Playwright binaries if: inputs.type == 'ui' uses: actions/cache@v3 @@ -212,6 +227,8 @@ jobs: sleep 10 done; + - name: Remove unused Docker images + run: docker system prune -af - name: E2E Test API if: inputs.type == 'api' working-directory: ./apps/meteor @@ -272,9 +289,9 @@ jobs: - name: Store playwright test trace if: inputs.type == 'ui' && always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: playwright-test-trace${{ inputs.release }} + name: playwright-test-trace-${{ matrix.mongodb-version }}-${{ matrix.shard }} path: ./apps/meteor/tests/e2e/.playwright* - name: Show server logs if E2E test failed @@ -305,14 +322,14 @@ jobs: - name: Store e2e-api-ee-coverage if: inputs.type == 'api' && inputs.release == 'ee' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: e2e-api-ee-coverage + name: e2e-api-ee-coverage-${{ matrix.mongodb-version }}-${{ matrix.shard }} path: /tmp/coverage - name: Store e2e-ee-coverage if: inputs.type == 'ui' && inputs.release == 'ee' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: e2e-ee-coverage + name: e2e-ee-coverage-${{ matrix.mongodb-version }}-${{ matrix.shard }} path: ./apps/meteor/coverage* diff --git a/.github/workflows/ci-test-unit.yml b/.github/workflows/ci-test-unit.yml index bfb22ffa4e731..a32c1e575b8f4 100644 --- a/.github/workflows/ci-test-unit.yml +++ b/.github/workflows/ci-test-unit.yml @@ -40,7 +40,7 @@ jobs: cache-modules: true install: true - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Unit Test run: yarn testunit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 411aa2cc5b1ad..66cf1afcccfbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,7 +161,7 @@ jobs: restore-keys: | vite-local-cache-${{ runner.os }}- - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Build Rocket.Chat Packages run: yarn build @@ -492,7 +492,7 @@ jobs: ref: ${{ github.ref }} - name: Restore build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build path: /tmp/build @@ -540,7 +540,7 @@ jobs: - uses: actions/checkout@v4 - name: Restore build - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build path: /tmp/build diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml index f10578d5879f2..5ef8027b14676 100644 --- a/.github/workflows/new-release.yml +++ b/.github/workflows/new-release.yml @@ -38,7 +38,7 @@ jobs: cache-modules: true install: true - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Build packages run: yarn build diff --git a/.github/workflows/pr-update-description.yml b/.github/workflows/pr-update-description.yml index 71b4ffeda801e..e792127eac9dd 100644 --- a/.github/workflows/pr-update-description.yml +++ b/.github/workflows/pr-update-description.yml @@ -25,7 +25,7 @@ jobs: cache-modules: true install: true - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Build packages run: yarn build @@ -36,4 +36,3 @@ jobs: action: update-pr-description env: GITHUB_TOKEN: ${{ secrets.CI_PAT }} - diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e133a3153722d..ccc3408e194e2 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -28,7 +28,7 @@ jobs: cache-modules: true install: true - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + - uses: rharkor/caching-for-turbo@v1.5 - name: Build packages run: yarn build diff --git a/README.md b/README.md index 64dec811e1ca7..56e38c111e97e 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ yarn dsv # run only meteor (front and back) with pre-built packages After initialized, you can access the server at http://localhost:3000 +More details at: [Developer Docs](https://developer.rocket.chat/v1/docs/server-environment-setup) +PS: For Windows you MUST use WSL2 and have +12Gb RAM + + # Gitpod Setup 1. Click the button below to open this project in Gitpod. diff --git a/apps/meteor/.meteorMocks/index.ts b/apps/meteor/.meteorMocks/index.ts deleted file mode 100644 index e70ffa7f7c46d..0000000000000 --- a/apps/meteor/.meteorMocks/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import sinon from 'sinon'; - -export const Meteor = { - loginWithSamlToken: sinon.stub(), -}; diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index a3458db7863ba..c80c36bf4890a 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,287 @@ # @rocket.chat/meteor +## 6.11.0-rc.2 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.2 + - @rocket.chat/rest-typings@6.11.0-rc.2 + - @rocket.chat/api-client@0.2.3-rc.2 + - @rocket.chat/license@0.2.3-rc.2 + - @rocket.chat/omnichannel-services@0.3.0-rc.2 + - @rocket.chat/pdf-worker@0.2.0-rc.2 + - @rocket.chat/presence@0.2.3-rc.2 + - @rocket.chat/apps@0.1.3-rc.2 + - @rocket.chat/core-services@0.5.0-rc.2 + - @rocket.chat/cron@0.1.3-rc.2 + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.2 + - @rocket.chat/gazzodown@9.0.0-rc.2 + - @rocket.chat/model-typings@0.6.0-rc.2 + - @rocket.chat/ui-contexts@9.0.0-rc.2 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/models@0.2.0-rc.2 + - @rocket.chat/ui-theming@0.2.0 + - @rocket.chat/ui-avatar@5.0.0-rc.2 + - @rocket.chat/ui-client@9.0.0-rc.2 + - @rocket.chat/ui-video-conf@9.0.0-rc.2 + - @rocket.chat/web-ui-registration@9.0.0-rc.2 + - @rocket.chat/instance-status@0.1.3-rc.2 +
+ +## 6.11.0-rc.1 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.1 + - @rocket.chat/rest-typings@6.11.0-rc.1 + - @rocket.chat/api-client@0.2.2-rc.1 + - @rocket.chat/license@0.2.2-rc.1 + - @rocket.chat/omnichannel-services@0.3.0-rc.1 + - @rocket.chat/pdf-worker@0.2.0-rc.1 + - @rocket.chat/presence@0.2.2-rc.1 + - @rocket.chat/apps@0.1.2-rc.1 + - @rocket.chat/core-services@0.5.0-rc.1 + - @rocket.chat/cron@0.1.2-rc.1 + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.1 + - @rocket.chat/gazzodown@9.0.0-rc.1 + - @rocket.chat/model-typings@0.6.0-rc.1 + - @rocket.chat/ui-contexts@9.0.0-rc.1 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/models@0.2.0-rc.1 + - @rocket.chat/ui-theming@0.2.0 + - @rocket.chat/ui-avatar@5.0.0-rc.1 + - @rocket.chat/ui-client@9.0.0-rc.1 + - @rocket.chat/ui-video-conf@9.0.0-rc.1 + - @rocket.chat/web-ui-registration@9.0.0-rc.1 + - @rocket.chat/instance-status@0.1.2-rc.1 +
+ +## 6.11.0-rc.0 + +### Minor Changes + +- ([#32498](https://github.com/RocketChat/Rocket.Chat/pull/32498)) Created a `transferChat` Livechat API endpoint for transferring chats programmatically, the endpoint has all the limitations & permissions required that transferring via UI has + +- ([#32792](https://github.com/RocketChat/Rocket.Chat/pull/32792)) Allows admins to customize the `Subject` field of Omnichannel email transcripts via setting. By passing a value to the setting `Custom email subject for transcript`, system will use it as the `Subject` field, unless a custom subject is passed when requesting a transcript. If there's no custom subject and setting value is empty, the current default value will be used + +- ([#32739](https://github.com/RocketChat/Rocket.Chat/pull/32739)) Fixed an issue where FCM actions did not respect environment's proxy settings + +- ([#32570](https://github.com/RocketChat/Rocket.Chat/pull/32570)) Login services button was not respecting the button color and text color settings. Implemented a fix to respect these settings and change the button colors accordingly. + + Added a warning on all settings which allow admins to change OAuth button colors, so that they can be alerted about WCAG (Web Content Accessibility Guidelines) compliance. + +- ([#32706](https://github.com/RocketChat/Rocket.Chat/pull/32706)) Added the possibility for apps to remove users from a room + +- ([#32517](https://github.com/RocketChat/Rocket.Chat/pull/32517)) Feature Preview: New Navigation - `Header` and `Contextualbar` size improvements consistent with the new global `NavBar` + +- ([#32493](https://github.com/RocketChat/Rocket.Chat/pull/32493)) Fixed Livechat rooms being displayed in the Engagement Dashboard's "Channels" tab + +- ([#32742](https://github.com/RocketChat/Rocket.Chat/pull/32742)) Fixed an issue where adding `OVERWRITE_SETTING_` for any setting wasn't immediately taking effect sometimes, and needed a server restart to reflect. + +- ([#32752](https://github.com/RocketChat/Rocket.Chat/pull/32752)) Added system messages support for Omnichannel PDF transcripts and email transcripts. Currently these transcripts don't render system messages and is shown as an empty message in PDF/email. This PR adds this support for all valid livechat system messages. + + Also added a new setting under transcripts, to toggle the inclusion of system messages in email and PDF transcripts. + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. +- ([#32176](https://github.com/RocketChat/Rocket.Chat/pull/32176)) Added a method to the Apps-Engine that allows apps to read multiple messages from a room + +- ([#32493](https://github.com/RocketChat/Rocket.Chat/pull/32493)) Improved Engagement Dashboard's "Channels" tab performance by not returning rooms that had no activity in the analyzed period + +- ([#32024](https://github.com/RocketChat/Rocket.Chat/pull/32024)) Implemented a new tab to the users page called 'Active', this tab lists all users who have logged in for the first time and are active. + +- ([#32744](https://github.com/RocketChat/Rocket.Chat/pull/32744)) Added account setting `Accounts_Default_User_Preferences_sidebarSectionsOrder` to allow users to reorganize sidebar sections + +- ([#32820](https://github.com/RocketChat/Rocket.Chat/pull/32820)) Added a new setting `Livechat_transcript_send_always` that allows admins to decide if email transcript should be sent all the times when a conversation is closed. This setting bypasses agent's preferences. For this setting to work, `Livechat_enable_transcript` should be off, meaning that visitors will no longer receive the option to decide if they want a transcript or not. + +- ([#32724](https://github.com/RocketChat/Rocket.Chat/pull/32724)) Extended apps-engine events for users leaving a room to also fire when being removed by another user. Also added the triggering user's information to the event's context payload. + +- ([#32777](https://github.com/RocketChat/Rocket.Chat/pull/32777)) Added handling of attachments in Omnichannel email transcripts. Earlier attachments were being skipped and were being shown as empty space, now it should render the image attachments and should show relevant error message for unsupported attachments. + +- ([#32800](https://github.com/RocketChat/Rocket.Chat/pull/32800)) Added the ability to filter chats by `queued` on the Current Chats Omnichannel page + +### Patch Changes + +- ([#32679](https://github.com/RocketChat/Rocket.Chat/pull/32679)) Fix validations from "UiKit" modal component + +- ([#32730](https://github.com/RocketChat/Rocket.Chat/pull/32730)) Fixed issue in Marketplace that caused a subscription app to show incorrect modals when subscribing + +- ([#32628](https://github.com/RocketChat/Rocket.Chat/pull/32628)) Fixed SAML users' full names being updated on login regardless of the "Overwrite user fullname (use idp attribute)" setting + +- ([#32692](https://github.com/RocketChat/Rocket.Chat/pull/32692)) Fixed an issue that caused the widget to set the wrong department when using the setDepartment Livechat api endpoint in conjunction with a Livechat Trigger + +- ([#32527](https://github.com/RocketChat/Rocket.Chat/pull/32527)) Fixed an inconsistent evaluation of the `Accounts_LoginExpiration` setting over the codebase. In some places, it was being used as milliseconds while in others as days. Invalid values produced different results. A helper function was created to centralize the setting validation and the proper value being returned to avoid edge cases. + Negative values may be saved on the settings UI panel but the code will interpret any negative, NaN or 0 value to the default expiration which is 90 days. +- ([#32626](https://github.com/RocketChat/Rocket.Chat/pull/32626)) livechat `setDepartment` livechat api fixes: + - Changing department didn't reflect on the registration form in real time + - Changing the department mid conversation didn't transfer the chat + - Depending on the state of the department, it couldn't be set as default +- ([#32810](https://github.com/RocketChat/Rocket.Chat/pull/32810)) Fixed issue where bad word filtering was not working in the UI for messages + +- ([#32707](https://github.com/RocketChat/Rocket.Chat/pull/32707)) Fixed issue with livechat agents not being able to leave omnichannel rooms if joining after a room has been closed by the visitor (due to race conditions) + +- ([#32837](https://github.com/RocketChat/Rocket.Chat/pull/32837)) Fixed an issue where non-encrypted attachments were not being downloaded + +- ([#32861](https://github.com/RocketChat/Rocket.Chat/pull/32861)) fixed the contextual bar closing when editing thread messages instead of cancelling the message edit + +- ([#32713](https://github.com/RocketChat/Rocket.Chat/pull/32713)) Fixed the disappearance of some settings after navigation under network latency. + +- ([#32592](https://github.com/RocketChat/Rocket.Chat/pull/32592)) Fixes Missing line breaks on Omnichannel Room Info Panel + +- ([#32807](https://github.com/RocketChat/Rocket.Chat/pull/32807)) Fixed web client crashing on Firefox private window. Firefox disables access to service workers inside private windows. Rocket.Chat needs service workers to process E2EE encrypted files on rooms. These types of files won't be available inside private windows, but the rest of E2EE encrypted features should work normally + +- ([#32864](https://github.com/RocketChat/Rocket.Chat/pull/32864)) fixed an issue in the "Create discussion" form, that would have the "Create" action button disabled even though the form is prefilled when opening it from the message action + +- ([#32691](https://github.com/RocketChat/Rocket.Chat/pull/32691)) Removed 'Hide' option in the room menu for Omnichannel conversations. + +- ([#32445](https://github.com/RocketChat/Rocket.Chat/pull/32445)) Fixed LDAP rooms, teams and roles syncs not being triggered on login even when the "Update User Data on Login" setting is enabled + +- ([#32328](https://github.com/RocketChat/Rocket.Chat/pull/32328)) Allow customFields on livechat creation bridge + +- ([#32803](https://github.com/RocketChat/Rocket.Chat/pull/32803)) Fixed "Copy link" message action enabled in Starred and Pinned list for End to End Encrypted channels, this action is disabled now + +- ([#32769](https://github.com/RocketChat/Rocket.Chat/pull/32769)) Fixed issue that caused unintentional clicks when scrolling the channels sidebar on safari/chrome in iOS + +- ([#32857](https://github.com/RocketChat/Rocket.Chat/pull/32857)) Fixed some anomalies related to disabled E2EE rooms. Earlier there are some weird issues with disabled E2EE rooms, this PR fixes these anomalies. + +- ([#32765](https://github.com/RocketChat/Rocket.Chat/pull/32765)) Fixed an issue that prevented the option to start a discussion from being shown on the message actions + +- ([#32671](https://github.com/RocketChat/Rocket.Chat/pull/32671)) Fix show correct user roles after updating user roles on admin edit user panel. + +- ([#32482](https://github.com/RocketChat/Rocket.Chat/pull/32482)) Fixed an issue with blocked login when dismissed 2FA modal by clicking outside of it or pressing the escape key + +- ([#32804](https://github.com/RocketChat/Rocket.Chat/pull/32804)) Fixes an issue not displaying all groups in settings list + +- ([#32815](https://github.com/RocketChat/Rocket.Chat/pull/32815)) Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +- ([#32632](https://github.com/RocketChat/Rocket.Chat/pull/32632)) Improving UX by change the position of room info actions buttons and menu order to avoid missclick in destructive actions. + +- ([#32752](https://github.com/RocketChat/Rocket.Chat/pull/32752)) Reduced time on generation of PDF transcripts. Earlier Rocket.Chat was fetching the required translations everytime a PDF transcript was requested, this process was async and was being unnecessarily being performed on every pdf transcript request. This PR improves this and now the translations are loaded at the start and kept in memory to process further pdf transcripts requests. This reduces the time of asynchronously fetching translations again and again. + +- ([#32719](https://github.com/RocketChat/Rocket.Chat/pull/32719)) Added the `user` param to apps-engine update method call, allowing apps' new `onUpdate` hook to know who triggered the update. + +-
Updated dependencies [88e5219bd2, b4bbcbfc9a, 8fc6ca8b4e, 15664127be, 25da5280a5, 1b7b1161cf, 439faa87d3, 03c8b066f9, 2d89a0c448, 439faa87d3, 24f7df4894, 3ffe4a2944, 3b4b19cfc5, 4e8aa575a6, 03c8b066f9, 264d7d5496, b8e5887fb9]: + + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.0 + - @rocket.chat/i18n@0.6.0-rc.0 + - @rocket.chat/tools@0.2.2-rc.0 + - @rocket.chat/web-ui-registration@9.0.0-rc.0 + - @rocket.chat/ui-client@9.0.0-rc.0 + - @rocket.chat/model-typings@0.6.0-rc.0 + - @rocket.chat/omnichannel-services@0.3.0-rc.0 + - @rocket.chat/pdf-worker@0.2.0-rc.0 + - @rocket.chat/core-services@0.5.0-rc.0 + - @rocket.chat/ui-video-conf@9.0.0-rc.0 + - @rocket.chat/core-typings@6.11.0-rc.0 + - @rocket.chat/ui-contexts@9.0.0-rc.0 + - @rocket.chat/models@0.2.0-rc.0 + - @rocket.chat/ui-kit@0.36.0-rc.0 + - @rocket.chat/rest-typings@6.11.0-rc.0 + - @rocket.chat/apps@0.1.2-rc.0 + - @rocket.chat/presence@0.2.2-rc.0 + - @rocket.chat/gazzodown@9.0.0-rc.0 + - @rocket.chat/api-client@0.2.2-rc.0 + - @rocket.chat/license@0.2.2-rc.0 + - @rocket.chat/cron@0.1.2-rc.0 + - @rocket.chat/ui-theming@0.2.0 + - @rocket.chat/ui-avatar@5.0.0-rc.0 + - @rocket.chat/instance-status@0.1.2-rc.0 + - @rocket.chat/server-cloud-communication@0.0.2 + +## 6.10.2 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that prevented apps from being updated or uninstalled in some cases + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that prevented apps from handling errors during execution in some cases + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Improved Apps-Engine installation to prevent start up errors on manual installation setups + +- ([#32950](https://github.com/RocketChat/Rocket.Chat/pull/32950) by [@dionisio-bot](https://github.com/dionisio-bot)) Fixed a crash on web client due to service workers not being available, this can happen in multiple scenarios like on Firefox's private window or if the connection is not secure (non-HTTPS), [see more details](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). + + Rocket.Chat needs service workers to process E2EE encrypted files on rooms. These types of files won't be available inside private windows, but the rest of E2EE encrypted features should work normally + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that caused the video conference button on rooms to not recognize a video conference provider app in some cases + +-
Updated dependencies [ca6a9d8de8, ca6a9d8de8, ca6a9d8de8, ca6a9d8de8]: + + - @rocket.chat/fuselage-ui-kit@8.0.2 + - @rocket.chat/core-services@0.4.2 + - @rocket.chat/core-typings@6.10.2 + - @rocket.chat/rest-typings@6.10.2 + - @rocket.chat/presence@0.2.2 + - @rocket.chat/apps@0.1.2 + - @rocket.chat/omnichannel-services@0.2.2 + - @rocket.chat/api-client@0.2.2 + - @rocket.chat/license@0.2.2 + - @rocket.chat/pdf-worker@0.1.2 + - @rocket.chat/cron@0.1.2 + - @rocket.chat/gazzodown@8.0.2 + - @rocket.chat/model-typings@0.5.2 + - @rocket.chat/ui-contexts@8.0.2 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/models@0.1.2 + - @rocket.chat/ui-theming@0.2.0 + - @rocket.chat/ui-avatar@4.0.2 + - @rocket.chat/ui-client@8.0.2 + - @rocket.chat/ui-video-conf@8.0.2 + - @rocket.chat/web-ui-registration@8.0.2 + - @rocket.chat/instance-status@0.1.2 +
+ +## 6.10.1 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +- Bump @rocket.chat/meteor version. + +- ([#32819](https://github.com/RocketChat/Rocket.Chat/pull/32819) by [@dionisio-bot](https://github.com/dionisio-bot)) Fixed issue with livechat agents not being able to leave omnichannel rooms if joining after a room has been closed by the visitor (due to race conditions) + +- ([#32894](https://github.com/RocketChat/Rocket.Chat/pull/32894)) Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates) + +- ([#32829](https://github.com/RocketChat/Rocket.Chat/pull/32829) by [@dionisio-bot](https://github.com/dionisio-bot)) Fixes an issue not displaying all groups in settings list + +- ([#32836](https://github.com/RocketChat/Rocket.Chat/pull/32836) by [@dionisio-bot](https://github.com/dionisio-bot)) Security Hotfix (https://docs.rocket.chat/guides/security/security-updates) + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.10.1 + - @rocket.chat/rest-typings@6.10.1 + - @rocket.chat/api-client@0.2.1 + - @rocket.chat/license@0.2.1 + - @rocket.chat/omnichannel-services@0.2.1 + - @rocket.chat/pdf-worker@0.1.1 + - @rocket.chat/presence@0.2.1 + - @rocket.chat/apps@0.1.1 + - @rocket.chat/core-services@0.4.1 + - @rocket.chat/cron@0.1.1 + - @rocket.chat/fuselage-ui-kit@8.0.1 + - @rocket.chat/gazzodown@8.0.1 + - @rocket.chat/model-typings@0.5.1 + - @rocket.chat/ui-contexts@8.0.1 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/models@0.1.1 + - @rocket.chat/ui-theming@0.2.0 + - @rocket.chat/ui-avatar@4.0.1 + - @rocket.chat/ui-client@8.0.1 + - @rocket.chat/ui-video-conf@8.0.1 + - @rocket.chat/web-ui-registration@8.0.1 + - @rocket.chat/instance-status@0.1.1 +
+ ## 6.10.0 ### Minor Changes diff --git a/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts b/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts index 219a78483cea6..f5f5b0e1f2758 100644 --- a/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts +++ b/apps/meteor/app/2fa/server/methods/checkCodesRemaining.ts @@ -1,7 +1,7 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { '2fa:checkCodesRemaining': () => { remaining: number }; diff --git a/apps/meteor/app/2fa/server/methods/disable.ts b/apps/meteor/app/2fa/server/methods/disable.ts index 0927b3f854ac2..d2c71febdc357 100644 --- a/apps/meteor/app/2fa/server/methods/disable.ts +++ b/apps/meteor/app/2fa/server/methods/disable.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { TOTP } from '../lib/totp'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { '2fa:disable': (code: string) => Promise; diff --git a/apps/meteor/app/2fa/server/methods/enable.ts b/apps/meteor/app/2fa/server/methods/enable.ts index 3b9f35dfcd9df..dea859d27b626 100644 --- a/apps/meteor/app/2fa/server/methods/enable.ts +++ b/apps/meteor/app/2fa/server/methods/enable.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { TOTP } from '../lib/totp'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { '2fa:enable': () => Promise<{ secret: string; url: string }>; @@ -34,6 +34,10 @@ Meteor.methods({ }); } + if (user.services?.totp?.enabled) { + throw new Meteor.Error('error-2fa-already-enabled'); + } + const secret = TOTP.generateSecret(); await Users.disable2FAAndSetTempSecretByUserId(userId, secret.base32); diff --git a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts index e0f4cfaa4797e..6968abfb93c2c 100644 --- a/apps/meteor/app/2fa/server/methods/regenerateCodes.ts +++ b/apps/meteor/app/2fa/server/methods/regenerateCodes.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { TOTP } from '../lib/totp'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { '2fa:regenerateCodes': (userToken: string) => { codes: string[] } | undefined; diff --git a/apps/meteor/app/2fa/server/methods/validateTempToken.ts b/apps/meteor/app/2fa/server/methods/validateTempToken.ts index cc2b44372a04e..840fadc8cbf7c 100644 --- a/apps/meteor/app/2fa/server/methods/validateTempToken.ts +++ b/apps/meteor/app/2fa/server/methods/validateTempToken.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { notifyOnUserChangeAsync } from '../../../lib/server/lib/notifyListener'; import { TOTP } from '../lib/totp'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { '2fa:validateTempToken': (userToken: string) => { codes: string[] } | undefined; diff --git a/apps/meteor/app/api/server/v1/chat.ts b/apps/meteor/app/api/server/v1/chat.ts index c482e3bb784d2..3ccc9caeafa00 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -18,6 +18,7 @@ import { executeUpdateMessage } from '../../../lib/server/methods/updateMessage' import { OEmbed } from '../../../oembed/server/server'; import { executeSetReaction } from '../../../reactions/server/setReaction'; import { settings } from '../../../settings/server'; +import { MessageTypes } from '../../../ui-utils/server'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; @@ -217,6 +218,10 @@ API.v1.addRoute( throw new Meteor.Error('error-invalid-params', 'The "message" parameter must be provided.'); } + if (MessageTypes.isSystemMessage(this.bodyParams.message)) { + throw new Error("Cannot send system messages using 'chat.sendMessage'"); + } + const sent = await executeSendMessage(this.userId, this.bodyParams.message as Pick, this.bodyParams.previewUrls); const [message] = await normalizeMessagesForUser([sent], this.userId); diff --git a/apps/meteor/app/api/server/v1/emoji-custom.ts b/apps/meteor/app/api/server/v1/emoji-custom.ts index a61149c5e66e8..9cbf202896e11 100644 --- a/apps/meteor/app/api/server/v1/emoji-custom.ts +++ b/apps/meteor/app/api/server/v1/emoji-custom.ts @@ -3,6 +3,8 @@ import { EmojiCustom } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../../server/lib/logger/system'; +import { insertOrUpdateEmoji } from '../../../emoji-custom/server/lib/insertOrUpdateEmoji'; +import { uploadEmojiCustomWithBuffer } from '../../../emoji-custom/server/lib/uploadEmojiCustom'; import { settings } from '../../../settings/server'; import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; @@ -148,9 +150,19 @@ API.v1.addRoute( fields.extension = emojiToUpdate.extension; } - await Meteor.callAsync('insertOrUpdateEmoji', { ...fields, newFile }); + const emojiData = { + name: fields.name, + _id: fields._id, + aliases: fields.aliases, + extension: fields.extension, + previousName: fields.previousName, + previousExtension: fields.previousExtension, + newFile, + }; + + await insertOrUpdateEmoji(this.userId, emojiData); if (fields.newFile) { - await Meteor.callAsync('uploadEmojiCustom', fileBuffer, mimetype, { ...fields, newFile }); + await uploadEmojiCustomWithBuffer(this.userId, fileBuffer, mimetype, emojiData); } return API.v1.success(); }, diff --git a/apps/meteor/app/api/server/v1/teams.ts b/apps/meteor/app/api/server/v1/teams.ts index 4ea8f2a48f389..f64f8c8205759 100644 --- a/apps/meteor/app/api/server/v1/teams.ts +++ b/apps/meteor/app/api/server/v1/teams.ts @@ -1,6 +1,6 @@ import { Team } from '@rocket.chat/core-services'; import type { ITeam, UserStatus } from '@rocket.chat/core-typings'; -import { TEAM_TYPE } from '@rocket.chat/core-typings'; +import { TEAM_TYPE, isValidSidepanel } from '@rocket.chat/core-typings'; import { Users, Rooms } from '@rocket.chat/models'; import { isTeamsConvertToChannelProps, @@ -85,7 +85,11 @@ API.v1.addRoute( }), ); - const { name, type, members, room, owner } = this.bodyParams; + const { name, type, members, room, owner, sidepanel } = this.bodyParams; + + if (sidepanel?.items && !isValidSidepanel(sidepanel)) { + throw new Error('error-invalid-sidepanel'); + } const team = await Team.create(this.userId, { team: { @@ -95,6 +99,7 @@ API.v1.addRoute( room, members, owner, + sidepanel, }); return API.v1.success({ team }); diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index c26957fa19910..7ae585b89dfa7 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -19,6 +19,7 @@ import { isUsersCheckUsernameAvailabilityParamsGET, isUsersSendConfirmationEmailParamsPOST, } from '@rocket.chat/rest-typings'; +import { getLoginExpirationInMs } from '@rocket.chat/tools'; import { Accounts } from 'meteor/accounts-base'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -43,6 +44,7 @@ import { setStatusText } from '../../../lib/server/functions/setStatusText'; import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; import { setUsernameWithValidation } from '../../../lib/server/functions/setUsername'; import { validateCustomFields } from '../../../lib/server/functions/validateCustomFields'; +import { validateNameChars } from '../../../lib/server/functions/validateNameChars'; import { notifyOnUserChange, notifyOnUserChangeAsync } from '../../../lib/server/lib/notifyListener'; import { generateAccessToken } from '../../../lib/server/methods/createToken'; import { settings } from '../../../settings/server'; @@ -94,6 +96,10 @@ API.v1.addRoute( async post() { const userData = { _id: this.bodyParams.userId, ...this.bodyParams.data }; + if (userData.name && !validateNameChars(userData.name)) { + return API.v1.failure('Name contains invalid characters'); + } + await saveUser(this.userId, userData); if (this.bodyParams.data.customFields) { @@ -138,6 +144,10 @@ API.v1.addRoute( typedPassword: this.bodyParams.data.currentPassword, }; + if (userData.realname && !validateNameChars(userData.realname)) { + return API.v1.failure('Name contains invalid characters'); + } + // saveUserProfile now uses the default two factor authentication procedures, so we need to provide that const twoFactorOptions = !userData.typedPassword ? null @@ -280,6 +290,10 @@ API.v1.addRoute( this.bodyParams.joinDefaultChannels = true; } + if (this.bodyParams.name && !validateNameChars(this.bodyParams.name)) { + return API.v1.failure('Name contains invalid characters'); + } + if (this.bodyParams.customFields) { validateCustomFields(this.bodyParams.customFields); } @@ -627,16 +641,20 @@ API.v1.addRoute( }, { async post() { + const { secret: secretURL, ...params } = this.bodyParams; + if (this.userId) { return API.v1.failure('Logged in users can not register again.'); } + if (params.name && !validateNameChars(params.name)) { + return API.v1.failure('Name contains invalid characters'); + } + if (!(await checkUsernameAvailability(this.bodyParams.username))) { return API.v1.failure('Username is already in use'); } - const { secret: secretURL, ...params } = this.bodyParams; - if (this.bodyParams.customFields) { try { await validateCustomFields(this.bodyParams.customFields); @@ -1048,8 +1066,9 @@ API.v1.addRoute( const token = me.services?.resume?.loginTokens?.find((token) => token.hashedToken === hashedToken); - const tokenExpires = - (token && 'when' in token && new Date(token.when.getTime() + settings.get('Accounts_LoginExpiration') * 1000)) || undefined; + const loginExp = settings.get('Accounts_LoginExpiration'); + + const tokenExpires = (token && 'when' in token && new Date(token.when.getTime() + getLoginExpirationInMs(loginExp))) || undefined; return API.v1.success({ token: xAuthToken, @@ -1197,7 +1216,7 @@ API.v1.addRoute( throw new Meteor.Error('error-invalid-user-id', 'Invalid user id'); } - void notifyOnUserChange({ clientAction: 'updated', id: this.userId, diff: { 'services.resume.loginTokens': [] } }); + void notifyOnUserChange({ clientAction: 'updated', id: userId, diff: { 'services.resume.loginTokens': [] } }); return API.v1.success({ message: `User ${userId} has been logged out!`, diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 4e2f8e69acadd..ec5cff29a99be 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -246,7 +246,8 @@ export class AppLivechatBridge extends LivechatBridge { username, name, type, - }; + userType: 'user', + } as const; let userId; let transferredTo; diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 86817c5721e2b..344acc74bda43 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -1,11 +1,13 @@ import type { IAppServerOrchestrator } from '@rocket.chat/apps'; -import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; +import type { IMessage, IMessageRaw } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; +import type { GetMessagesOptions } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; import { RoomBridge } from '@rocket.chat/apps-engine/server/bridges/RoomBridge'; -import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom } from '@rocket.chat/core-typings'; -import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import type { ISubscription, IUser as ICoreUser, IRoom as ICoreRoom, IMessage as ICoreMessage } from '@rocket.chat/core-typings'; +import { Subscriptions, Users, Rooms, Messages } from '@rocket.chat/models'; +import type { FindOptions, Sort } from 'mongodb'; import { createDirectMessage } from '../../../../server/methods/createDirectMessage'; import { createDiscussion } from '../../../discussion/server/methods/createDiscussion'; @@ -103,6 +105,38 @@ export class AppRoomBridge extends RoomBridge { return this.orch.getConverters()?.get('users').convertById(room.u._id); } + protected async getMessages(roomId: string, options: GetMessagesOptions, appId: string): Promise { + this.orch.debugLog(`The App ${appId} is getting the messages of the room: "${roomId}" with options:`, options); + + const { limit, skip = 0, sort: _sort } = options; + + const messageConverter = this.orch.getConverters()?.get('messages'); + if (!messageConverter) { + throw new Error('Message converter not found'); + } + + // We support only one field for now + const sort: Sort | undefined = _sort?.createdAt ? { ts: _sort.createdAt } : undefined; + + const messageQueryOptions: FindOptions = { + limit, + skip, + sort, + }; + + const query = { + rid: roomId, + _hidden: { $ne: true }, + t: { $exists: false }, + }; + + const cursor = Messages.find(query, messageQueryOptions); + + const messagePromises: Promise[] = await cursor.map((message) => messageConverter.convertMessageRaw(message)).toArray(); + + return Promise.all(messagePromises); + } + protected async getMembers(roomId: string, appId: string): Promise> { this.orch.debugLog(`The App ${appId} is getting the room's members by room id: "${roomId}"`); const subscriptions = await Subscriptions.findByRoomId(roomId, {}); @@ -220,12 +254,4 @@ export class AppRoomBridge extends RoomBridge { const members = await Users.findUsersByUsernames(usernames, { limit: 50 }).toArray(); await Promise.all(members.map((user) => removeUserFromRoom(roomId, user))); } - - protected getMessages( - _roomId: string, - _options: { limit: number; skip?: number; sort?: Record }, - _appId: string, - ): Promise { - throw new Error('Method not implemented.'); - } } diff --git a/apps/meteor/app/apps/server/bridges/videoConferences.ts b/apps/meteor/app/apps/server/bridges/videoConferences.ts index bebcb25a6f516..efab0f201f873 100644 --- a/apps/meteor/app/apps/server/bridges/videoConferences.ts +++ b/apps/meteor/app/apps/server/bridges/videoConferences.ts @@ -59,6 +59,10 @@ export class AppVideoConferenceBridge extends VideoConferenceBridge { if (data.status > oldData.status) { await VideoConf.setStatus(call._id, data.status); } + + if (data.discussionRid !== oldData.discussionRid) { + await VideoConf.assignDiscussionToConference(call._id, data.discussionRid); + } } protected async registerProvider(info: IVideoConfProvider, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/converters/cachedFunction.ts b/apps/meteor/app/apps/server/converters/cachedFunction.ts new file mode 100644 index 0000000000000..3310574f01608 --- /dev/null +++ b/apps/meteor/app/apps/server/converters/cachedFunction.ts @@ -0,0 +1,17 @@ +export const cachedFunction = any>(fn: F) => { + const cache = new Map(); + + return ((...args) => { + const cacheKey = JSON.stringify(args); + + if (cache.has(cacheKey)) { + return cache.get(cacheKey) as ReturnType; + } + + const result = fn(...args); + + cache.set(cacheKey, result); + + return result; + }) as F; +}; diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 187a6519339a4..d7dae512e9a8f 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,9 +1,13 @@ +import { isMessageFromVisitor } from '@rocket.chat/core-typings'; import { Messages, Rooms, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; +import { cachedFunction } from './cachedFunction'; import { transformMappedData } from './transformMappedData'; export class AppMessagesConverter { + mem = new WeakMap(); + constructor(orch) { this.orch = orch; } @@ -14,11 +18,54 @@ export class AppMessagesConverter { return this.convertMessage(msg); } + async convertMessageRaw(msgObj) { + if (!msgObj) { + return undefined; + } + + const { attachments, ...message } = msgObj; + const getAttachments = async () => this._convertAttachmentsToApp(attachments); + + const map = { + id: '_id', + threadId: 'tmid', + reactions: 'reactions', + parseUrls: 'parseUrls', + text: 'msg', + createdAt: 'ts', + updatedAt: '_updatedAt', + editedAt: 'editedAt', + emoji: 'emoji', + avatarUrl: 'avatar', + alias: 'alias', + file: 'file', + customFields: 'customFields', + groupable: 'groupable', + token: 'token', + blocks: 'blocks', + roomId: 'rid', + editor: 'editedBy', + attachments: getAttachments, + sender: 'u', + }; + + return transformMappedData(message, map); + } + async convertMessage(msgObj) { if (!msgObj) { return undefined; } + const cache = + this.mem.get(msgObj) ?? + new Map([ + ['room', cachedFunction(this.orch.getConverters().get('rooms').convertById.bind(this.orch.getConverters().get('rooms')))], + ['user', cachedFunction(this.orch.getConverters().get('users').convertById.bind(this.orch.getConverters().get('users')))], + ]); + + this.mem.set(msgObj, cache); + const map = { id: '_id', threadId: 'tmid', @@ -37,7 +84,7 @@ export class AppMessagesConverter { token: 'token', blocks: 'blocks', room: async (message) => { - const result = await this.orch.getConverters().get('rooms').convertById(message.rid); + const result = await cache.get('room')(message.rid); delete message.rid; return result; }, @@ -49,7 +96,7 @@ export class AppMessagesConverter { return undefined; } - return this.orch.getConverters().get('users').convertById(editedBy._id); + return cache.get('user')(editedBy._id); }, attachments: async (message) => { const result = await this._convertAttachmentsToApp(message.attachments); @@ -61,16 +108,19 @@ export class AppMessagesConverter { return undefined; } - let user = await this.orch.getConverters().get('users').convertById(message.u._id); - - // When the sender of the message is a Guest (livechat) and not a user - if (!user) { - user = this.orch.getConverters().get('users').convertToApp(message.u); - } + // When the message contains token, means the message is from the visitor(omnichannel) + const user = await (isMessageFromVisitor(msgObj) + ? this.orch.getConverters().get('users').convertToApp(message.u) + : cache.get('user')(message.u._id)); delete message.u; - return user; + /** + * Old System Messages from visitor doesn't have the `token` field, to not return + * `sender` as undefined, so we need to add this fallback here. + */ + + return user || this.orch.getConverters().get('users').convertToApp(message.u); }, }; diff --git a/apps/meteor/app/apps/server/converters/threads.ts b/apps/meteor/app/apps/server/converters/threads.ts index 840f4f1613ebb..e31ee094b4d73 100644 --- a/apps/meteor/app/apps/server/converters/threads.ts +++ b/apps/meteor/app/apps/server/converters/threads.ts @@ -5,6 +5,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { isEditedMessage, type IMessage } from '@rocket.chat/core-typings'; import { Messages } from '@rocket.chat/models'; +import { cachedFunction } from './cachedFunction'; import { transformMappedData } from './transformMappedData'; // eslint-disable-next-line @typescript-eslint/naming-convention @@ -18,24 +19,6 @@ interface Orchestrator { }; } -const cachedFunction = any>(fn: F) => { - const cache = new Map(); - - return ((...args) => { - const cacheKey = JSON.stringify(args); - - if (cache.has(cacheKey)) { - return cache.get(cacheKey) as ReturnType; - } - - const result = fn(...args); - - cache.set(cacheKey, result); - - return result; - }) as F; -}; - export class AppThreadsConverter implements IAppThreadsConverter { constructor( private readonly orch: { diff --git a/apps/meteor/app/assets/server/assets.ts b/apps/meteor/app/assets/server/assets.ts index b9653a2f1b9a9..5d627a4b1d295 100644 --- a/apps/meteor/app/assets/server/assets.ts +++ b/apps/meteor/app/assets/server/assets.ts @@ -2,8 +2,8 @@ import crypto from 'crypto'; import type { ServerResponse, IncomingMessage } from 'http'; import type { IRocketChatAssets, IRocketChatAsset, ISetting } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { NextHandleFunction } from 'connect'; import sizeOf from 'image-size'; import { Meteor } from 'meteor/meteor'; @@ -406,7 +406,7 @@ Meteor.startup(() => { }, 200); }); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { refreshClients(): boolean; diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index bffbe1f9876dd..2e4c599ce558c 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -2,6 +2,7 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import { User } from '@rocket.chat/core-services'; import { Roles, Settings, Users } from '@rocket.chat/models'; import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers'; +import { getLoginExpirationInDays } from '@rocket.chat/tools'; import { Accounts } from 'meteor/accounts-base'; import { Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -31,7 +32,7 @@ Accounts.config({ Meteor.startup(() => { settings.watchMultiple(['Accounts_LoginExpiration', 'Site_Name', 'From_Email'], () => { - Accounts._options.loginExpirationInDays = settings.get('Accounts_LoginExpiration'); + Accounts._options.loginExpirationInDays = getLoginExpirationInDays(settings.get('Accounts_LoginExpiration')); Accounts.emailTemplates.siteName = settings.get('Site_Name'); diff --git a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts index 13a114732bd28..9a336478ca3ec 100644 --- a/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addPermissionToRole.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Permissions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { notifyOnPermissionChangedById } from '../../../lib/server/lib/notifyListener'; import { CONSTANTS, AuthorizationUtils } from '../../lib'; import { hasPermissionAsync } from '../functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'authorization:addPermissionToRole'(permissionId: string, role: string): void; diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts index 02ccc76373f1e..81582dd7e9fb8 100644 --- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts @@ -1,14 +1,14 @@ import { api } from '@rocket.chat/core-services'; import type { IRole, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'authorization:addUserToRole'(roleId: IRole['_id'], username: IUser['username'], scope: string | undefined): Promise; diff --git a/apps/meteor/app/authorization/server/methods/deleteRole.ts b/apps/meteor/app/authorization/server/methods/deleteRole.ts index 2ff09deadf682..140852e0f1ecf 100644 --- a/apps/meteor/app/authorization/server/methods/deleteRole.ts +++ b/apps/meteor/app/authorization/server/methods/deleteRole.ts @@ -1,13 +1,13 @@ import type { IRole } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { hasPermissionAsync } from '../functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'authorization:deleteRole'(roleId: IRole['_id'] | IRole['name']): Promise; diff --git a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts index 91a4df1eddf75..68ca11ef9fb5f 100644 --- a/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts +++ b/apps/meteor/app/authorization/server/methods/removeRoleFromPermission.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Permissions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { notifyOnPermissionChangedById } from '../../../lib/server/lib/notifyListener'; import { CONSTANTS } from '../../lib'; import { hasPermissionAsync } from '../functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'authorization:removeRoleFromPermission'(permissionId: string, role: string): void; diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts index d5dba40a1e535..56f0c2e307abf 100644 --- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts +++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts @@ -1,14 +1,14 @@ import { api } from '@rocket.chat/core-services'; import type { IRole, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'authorization:removeUserFromRole'(roleId: IRole['_id'], username: IUser['username'], scope?: string): Promise; diff --git a/apps/meteor/app/authorization/server/streamer/permissions/index.ts b/apps/meteor/app/authorization/server/streamer/permissions/index.ts index ff8fd3c932626..e74cf37869fd8 100644 --- a/apps/meteor/app/authorization/server/streamer/permissions/index.ts +++ b/apps/meteor/app/authorization/server/streamer/permissions/index.ts @@ -1,11 +1,11 @@ import type { IPermission, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Permissions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { WithId } from 'mongodb'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'permissions/get'( diff --git a/apps/meteor/app/autotranslate/server/autotranslate.ts b/apps/meteor/app/autotranslate/server/autotranslate.ts index 7a9eb8780a2df..1e6c224c41157 100644 --- a/apps/meteor/app/autotranslate/server/autotranslate.ts +++ b/apps/meteor/app/autotranslate/server/autotranslate.ts @@ -15,7 +15,7 @@ import _ from 'underscore'; import { callbacks } from '../../../lib/callbacks'; import { isTruthy } from '../../../lib/isTruthy'; -import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; +import { notifyOnMessageChange } from '../../lib/server/lib/notifyListener'; import { Markdown } from '../../markdown/server'; import { settings } from '../../settings/server'; @@ -332,7 +332,7 @@ export abstract class AutoTranslate { } private notifyTranslatedMessage(messageId: string): void { - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: messageId, }); } diff --git a/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts b/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts index 30760e854ed1e..1d443c21d210f 100644 --- a/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts +++ b/apps/meteor/app/autotranslate/server/methods/getProviderUiMetadata.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { TranslationProviderRegistry } from '../autotranslate'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'autoTranslate.getProviderUiMetadata'(): Record; diff --git a/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts b/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts index e0118e8d95a49..7b9614da7739c 100644 --- a/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts +++ b/apps/meteor/app/autotranslate/server/methods/getSupportedLanguages.ts @@ -1,5 +1,5 @@ import type { ISupportedLanguage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import { Meteor } from 'meteor/meteor'; @@ -7,7 +7,7 @@ import { TranslationProviderRegistry } from '..'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'autoTranslate.getSupportedLanguages'(targetLanguage: string): ISupportedLanguage[] | undefined; diff --git a/apps/meteor/app/autotranslate/server/methods/saveSettings.ts b/apps/meteor/app/autotranslate/server/methods/saveSettings.ts index e396d78887a91..2f119c9482634 100644 --- a/apps/meteor/app/autotranslate/server/methods/saveSettings.ts +++ b/apps/meteor/app/autotranslate/server/methods/saveSettings.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'autoTranslate.saveSettings'(rid: string, field: string, value: string, options: { defaultLanguage: string }): boolean; diff --git a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts index d90cad90ce777..551eba57c005e 100644 --- a/apps/meteor/app/autotranslate/server/methods/translateMessage.ts +++ b/apps/meteor/app/autotranslate/server/methods/translateMessage.ts @@ -1,11 +1,11 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { TranslationProviderRegistry } from '..'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'autoTranslate.translateMessage'(message: IMessage | undefined, targetLanguage: string): Promise; diff --git a/apps/meteor/app/bot-helpers/server/index.ts b/apps/meteor/app/bot-helpers/server/index.ts index 5e19e16524543..6c0984ae483d9 100644 --- a/apps/meteor/app/bot-helpers/server/index.ts +++ b/apps/meteor/app/bot-helpers/server/index.ts @@ -1,7 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { UserStatus } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import type { Filter, FindCursor } from 'mongodb'; @@ -194,7 +194,7 @@ settings.watch('BotHelpers_userFields', (value) => { botHelpers.setupCursors(value); }); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { botRequest: (prop: keyof BotHelpers, ...params: unknown[]) => Promise; diff --git a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts index e17faebea384d..44ad253d83efd 100644 --- a/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts +++ b/apps/meteor/app/channel-settings/server/methods/saveRoomSettings.ts @@ -1,8 +1,8 @@ import { Team } from '@rocket.chat/core-services'; import type { IRoom, IRoomWithRetentionPolicy, IUser, MessageTypesValues } from '@rocket.chat/core-typings'; -import { TEAM_TYPE } from '@rocket.chat/core-typings'; +import { TEAM_TYPE, isValidSidepanel } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -49,6 +49,7 @@ type RoomSettings = { favorite: boolean; defaultValue: boolean; }; + sidepanel?: IRoom['sidepanel']; }; type RoomSettingsValidators = { @@ -80,6 +81,24 @@ const validators: RoomSettingsValidators = { }); } }, + async sidepanel({ room, userId, value }) { + if (!room.teamMain) { + throw new Meteor.Error('error-action-not-allowed', 'Invalid room', { + method: 'saveRoomSettings', + }); + } + + if (!(await hasPermissionAsync(userId, 'edit-team', room._id))) { + throw new Meteor.Error('error-action-not-allowed', 'You do not have permission to change sidepanel items', { + method: 'saveRoomSettings', + }); + } + + if (!isValidSidepanel(value)) { + throw new Meteor.Error('error-invalid-sidepanel'); + } + }, + async roomType({ userId, room, value }) { if (value === room.t) { return; @@ -213,6 +232,11 @@ const settingSavers: RoomSettingsSavers = { await saveRoomTopic(rid, value, user); } }, + async sidepanel({ value, rid, room }) { + if (JSON.stringify(value) !== JSON.stringify(room.sidepanel)) { + await Rooms.setSidepanelById(rid, value); + } + }, async roomAnnouncement({ value, room, rid, user }) { if (!value && !room.announcement) { return; @@ -304,7 +328,7 @@ const settingSavers: RoomSettingsSavers = { }, }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { saveRoomSettings(rid: IRoom['_id'], settings: Partial): Promise<{ result: true; rid: IRoom['_id'] }>; @@ -339,6 +363,7 @@ const fields: (keyof RoomSettings)[] = [ 'retentionOverrideGlobal', 'encrypted', 'favorite', + 'sidepanel', ]; const validate = ( diff --git a/apps/meteor/app/cloud/server/methods.ts b/apps/meteor/app/cloud/server/methods.ts index 0e47a8ba8754e..29daefe0d58ca 100644 --- a/apps/meteor/app/cloud/server/methods.ts +++ b/apps/meteor/app/cloud/server/methods.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -13,7 +13,7 @@ import { startRegisterWorkspace } from './functions/startRegisterWorkspace'; import { syncWorkspace } from './functions/syncWorkspace'; import { userLogout } from './functions/userLogout'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'cloud:checkRegisterStatus': () => { diff --git a/apps/meteor/app/crowd/server/methods.ts b/apps/meteor/app/crowd/server/methods.ts index a621e3c8d0273..48faa2fcbcabc 100644 --- a/apps/meteor/app/crowd/server/methods.ts +++ b/apps/meteor/app/crowd/server/methods.ts @@ -1,4 +1,5 @@ -import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; @@ -6,7 +7,7 @@ import { settings } from '../../settings/server'; import { CROWD } from './crowd'; import { logger } from './logger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { crowd_test_connection(): { message: TranslationKey; params: string[] }; diff --git a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts index 5ddf0cc66e52b..1c63c7a67d9fc 100644 --- a/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/deleteCustomSound.ts @@ -1,13 +1,13 @@ import { api } from '@rocket.chat/core-services'; import type { ICustomSound } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { CustomSounds } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteCustomSound(_id: ICustomSound['_id']): Promise; diff --git a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts index 4b48a64dc14cb..1b922c6b162ed 100644 --- a/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/insertOrUpdateSound.ts @@ -1,6 +1,6 @@ import { api } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { CustomSounds } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -20,7 +20,7 @@ export type ICustomSoundData = { random?: number; }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { insertOrUpdateSound(soundData: ICustomSoundData): Promise; diff --git a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts index 2c2130fc0d30f..eda1325d77335 100644 --- a/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts +++ b/apps/meteor/app/custom-sounds/server/methods/listCustomSounds.ts @@ -1,9 +1,9 @@ import type { ICustomSound } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { CustomSounds } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { listCustomSounds(): ICustomSound[]; diff --git a/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts b/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts index eee693634d7df..f955f373ed4d4 100644 --- a/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts +++ b/apps/meteor/app/custom-sounds/server/methods/uploadCustomSound.ts @@ -1,6 +1,6 @@ import { api } from '@rocket.chat/core-services'; import type { RequiredField } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -8,7 +8,7 @@ import { RocketChatFile } from '../../../file/server'; import { RocketChatFileCustomSoundsInstance } from '../startup/custom-sounds'; import type { ICustomSoundData } from './insertOrUpdateSound'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { uploadCustomSound(binaryContent: string, contentType: string, soundData: RequiredField): void; diff --git a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts index 3ad61c4c42f06..ecf0142488308 100644 --- a/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts +++ b/apps/meteor/app/discussion/client/createDiscussionMessageAction.ts @@ -44,7 +44,7 @@ Meteor.startup(() => { subscription, user, }) { - if (drid || !Number.isNaN(dcount)) { + if (drid || !Number.isNaN(Number(dcount))) { return false; } if (!subscription) { diff --git a/apps/meteor/app/discussion/client/index.ts b/apps/meteor/app/discussion/client/index.ts index 62e11191b4936..7c0a6f72e6cc0 100644 --- a/apps/meteor/app/discussion/client/index.ts +++ b/apps/meteor/app/discussion/client/index.ts @@ -1,3 +1,2 @@ // Other UI extensions -import './lib/messageTypes/discussionMessage'; import './createDiscussionMessageAction'; diff --git a/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js b/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js deleted file mode 100644 index a7f0ef0a1d977..0000000000000 --- a/apps/meteor/app/discussion/client/lib/messageTypes/discussionMessage.js +++ /dev/null @@ -1,16 +0,0 @@ -import { Meteor } from 'meteor/meteor'; - -import { MessageTypes } from '../../../../ui-utils/client'; - -Meteor.startup(() => { - MessageTypes.registerType({ - id: 'discussion-created', - system: false, - message: 'discussion-created', - data(message) { - return { - message: ` ${message.msg}`, - }; - }, - }); -}); diff --git a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts index 0f42f495e9626..05cf2326156f8 100644 --- a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts +++ b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts @@ -1,16 +1,16 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { Messages, Rooms } from '@rocket.chat/models'; +import { Messages, Rooms, VideoConference } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; +import { notifyOnMessageChange } from '../../../lib/server/lib/notifyListener'; const updateAndNotifyParentRoomWithParentMessage = async (room: IRoom): Promise => { const { value: parentMessage } = await Messages.refreshDiscussionMetadata(room); if (!parentMessage) { return; } - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: parentMessage._id, data: parentMessage, }); @@ -108,6 +108,8 @@ callbacks.add( }, }, ); + + await VideoConference.unsetDiscussionRid(drid); return drid; }, callbacks.priority.LOW, diff --git a/apps/meteor/app/discussion/server/methods/createDiscussion.ts b/apps/meteor/app/discussion/server/methods/createDiscussion.ts index 18b42ba1a31f2..6e670d723ec92 100644 --- a/apps/meteor/app/discussion/server/methods/createDiscussion.ts +++ b/apps/meteor/app/discussion/server/methods/createDiscussion.ts @@ -1,8 +1,8 @@ import { Message } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser, MessageAttachmentDefault } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Rooms, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; @@ -196,7 +196,7 @@ const create = async ({ return discussion; }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { createDiscussion: typeof create; diff --git a/apps/meteor/app/e2e/client/rocketchat.e2e.ts b/apps/meteor/app/e2e/client/rocketchat.e2e.ts index 0cc344ff51527..bbd6f208f35a5 100644 --- a/apps/meteor/app/e2e/client/rocketchat.e2e.ts +++ b/apps/meteor/app/e2e/client/rocketchat.e2e.ts @@ -257,7 +257,7 @@ class E2E extends Emitter { return null; } - if (room.encrypted !== true && !room.e2eKeyId) { + if (!room.encrypted) { return null; } @@ -272,7 +272,7 @@ class E2E extends Emitter { delete this.instancesByRoomId[rid]; } - async persistKeys( + private async persistKeys( { public_key, private_key }: KeyPair, password: string, { force }: { force: boolean } = { force: false }, diff --git a/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts b/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts index 519317cb40fe4..df1150cee8463 100644 --- a/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts +++ b/apps/meteor/app/e2e/server/methods/fetchMyKeys.ts @@ -1,8 +1,8 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.fetchMyKeys'(): { public_key?: string; private_key?: string }; diff --git a/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts b/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts index cc586676482fe..1f1a21262de86 100644 --- a/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts +++ b/apps/meteor/app/e2e/server/methods/getUsersOfRoomWithoutKey.ts @@ -1,12 +1,12 @@ import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.getUsersOfRoomWithoutKey'(rid: IRoom['_id']): { users: Pick[] }; diff --git a/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts b/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts index 8c5add77a0bfb..cf899a5d64ad7 100644 --- a/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts +++ b/apps/meteor/app/e2e/server/methods/requestSubscriptionKeys.ts @@ -1,9 +1,9 @@ import { api } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.requestSubscriptionKeys'(): boolean; diff --git a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts index 365feb1ac95db..b1d40e48bb5e7 100644 --- a/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts +++ b/apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts @@ -1,10 +1,10 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.resetOwnE2EKey'(): Promise; diff --git a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts index 6b0e685616b5c..b52913e4f9849 100644 --- a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts +++ b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts @@ -1,13 +1,13 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { notifyOnRoomChangedById } from '../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.setRoomKeyID'(rid: IRoom['_id'], keyID: string): void; diff --git a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts index 94d252601bc49..6ef35a063a281 100644 --- a/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts +++ b/apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts @@ -1,8 +1,8 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }: { public_key: string; private_key: string; force?: boolean }): void; diff --git a/apps/meteor/app/e2e/server/methods/updateGroupKey.ts b/apps/meteor/app/e2e/server/methods/updateGroupKey.ts index c856f8cf708a6..5764a021f54ce 100644 --- a/apps/meteor/app/e2e/server/methods/updateGroupKey.ts +++ b/apps/meteor/app/e2e/server/methods/updateGroupKey.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'e2e.updateGroupKey'(rid: string, uid: string, key: string): Promise; diff --git a/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts new file mode 100644 index 0000000000000..7e838baee9b03 --- /dev/null +++ b/apps/meteor/app/emoji-custom/server/lib/insertOrUpdateEmoji.ts @@ -0,0 +1,148 @@ +import { api } from '@rocket.chat/core-services'; +import { EmojiCustom } from '@rocket.chat/models'; +import limax from 'limax'; +import { Meteor } from 'meteor/meteor'; +import _ from 'underscore'; + +import { trim } from '../../../../lib/utils/stringUtils'; +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; + +type EmojiData = { + _id?: string; + name: string; + aliases: string; + extension: string; + previousName?: string; + previousExtension?: string; + newFile?: boolean; +}; + +type EmojiDataWithParsedAliases = Omit & { _id: string; aliases: string[] }; + +export async function insertOrUpdateEmoji(userId: string | null, emojiData: EmojiData): Promise { + if (!userId || !(await hasPermissionAsync(userId, 'manage-emoji'))) { + throw new Meteor.Error('not_authorized'); + } + + if (!trim(emojiData.name)) { + throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { + method: 'insertOrUpdateEmoji', + field: 'Name', + }); + } + + emojiData.name = limax(emojiData.name, { replacement: '_' }); + emojiData.aliases = limax(emojiData.aliases, { replacement: '_' }); + + // allow all characters except colon, whitespace, comma, >, <, &, ", ', /, \, (, ) + // more practical than allowing specific sets of characters; also allows foreign languages + const nameValidation = /[\s,:><&"'\/\\\(\)]/; + const aliasValidation = /[:><&\|"'\/\\\(\)]/; + + // silently strip colon; this allows for uploading :emojiname: as emojiname + emojiData.name = emojiData.name.replace(/:/g, ''); + emojiData.aliases = emojiData.aliases.replace(/:/g, ''); + + if (nameValidation.test(emojiData.name)) { + throw new Meteor.Error('error-input-is-not-a-valid-field', `${emojiData.name} is not a valid name`, { + method: 'insertOrUpdateEmoji', + input: emojiData.name, + field: 'Name', + }); + } + + let aliases: string[] = []; + if (emojiData.aliases) { + if (aliasValidation.test(emojiData.aliases)) { + throw new Meteor.Error('error-input-is-not-a-valid-field', `${emojiData.aliases} is not a valid alias set`, { + method: 'insertOrUpdateEmoji', + input: emojiData.aliases, + field: 'Alias_Set', + }); + } + aliases = _.without(emojiData.aliases.split(/[\s,]/).filter(Boolean), emojiData.name); + } + + emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; + + let matchingResults = []; + + if (emojiData._id) { + matchingResults = await EmojiCustom.findByNameOrAliasExceptID(emojiData.name, emojiData._id).toArray(); + for await (const alias of aliases) { + matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAliasExceptID(alias, emojiData._id).toArray()); + } + } else { + matchingResults = await EmojiCustom.findByNameOrAlias(emojiData.name).toArray(); + for await (const alias of aliases) { + matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAlias(alias).toArray()); + } + } + + if (matchingResults.length > 0) { + throw new Meteor.Error('Custom_Emoji_Error_Name_Or_Alias_Already_In_Use', 'The custom emoji or one of its aliases is already in use', { + method: 'insertOrUpdateEmoji', + }); + } + + if (typeof emojiData.extension === 'undefined') { + throw new Meteor.Error('error-the-field-is-required', 'The custom emoji file is required', { + method: 'insertOrUpdateEmoji', + }); + } + + if (!emojiData._id) { + // insert emoji + const createEmoji = { + name: emojiData.name, + aliases, + extension: emojiData.extension, + }; + + const _id = (await EmojiCustom.create(createEmoji)).insertedId; + + void api.broadcast('emoji.updateCustom', createEmoji); + + return { ...emojiData, ...createEmoji, _id }; + } + + // update emoji + if (emojiData.newFile) { + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.previousExtension}`)); + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.extension}`)); + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`)); + + await EmojiCustom.setExtension(emojiData._id, emojiData.extension); + } else if (emojiData.name !== emojiData.previousName) { + const rs = await RocketChatFileEmojiCustomInstance.getFileWithReadStream( + encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`), + ); + if (rs !== null) { + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); + const ws = RocketChatFileEmojiCustomInstance.createWriteStream( + encodeURIComponent(`${emojiData.name}.${emojiData.previousExtension}`), + rs.contentType, + ); + ws.on('end', () => + RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`)), + ); + rs.readStream.pipe(ws); + } + } + + if (emojiData.name !== emojiData.previousName) { + await EmojiCustom.setName(emojiData._id, emojiData.name); + } + + if (emojiData.aliases) { + await EmojiCustom.setAliases(emojiData._id, aliases); + } else { + await EmojiCustom.setAliases(emojiData._id, []); + } + + void api.broadcast('emoji.updateCustom', { ...emojiData, aliases }); + + return { ...emojiData, aliases } as EmojiDataWithParsedAliases; +} diff --git a/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts new file mode 100644 index 0000000000000..07633eaa1a7da --- /dev/null +++ b/apps/meteor/app/emoji-custom/server/lib/uploadEmojiCustom.ts @@ -0,0 +1,77 @@ +import { api, Media } from '@rocket.chat/core-services'; +import limax from 'limax'; +import { Meteor } from 'meteor/meteor'; +import sharp from 'sharp'; + +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { RocketChatFile } from '../../../file/server'; +import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; + +const getFile = async (file: Buffer, extension: string) => { + if (extension !== 'svg+xml') { + return file; + } + + return sharp(file).png().toBuffer(); +}; + +type EmojiData = { + _id?: string; + name: string; + aliases?: string | string[]; + extension: string; + previousName?: string; + previousExtension?: string; + newFile?: boolean; +}; + +export async function uploadEmojiCustom(userId: string | null, binaryContent: string, contentType: string, emojiData: EmojiData) { + return uploadEmojiCustomWithBuffer(userId, Buffer.from(binaryContent, 'binary'), contentType, emojiData); +} + +export async function uploadEmojiCustomWithBuffer( + userId: string | null, + buffer: Buffer, + contentType: string, + emojiData: EmojiData, +): Promise { + // technically, since this method doesnt have any datatype validations, users can + // upload videos as emojis. The FE won't play them, but they will waste space for sure. + if (!userId || !(await hasPermissionAsync(userId, 'manage-emoji'))) { + throw new Meteor.Error('not_authorized'); + } + + emojiData.name = limax(emojiData.name, { replacement: '_' }); + // delete aliases for notification purposes. here, it is a string rather than an array + delete emojiData.aliases; + + const file = await getFile(buffer, emojiData.extension); + emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; + + let fileBuffer; + // sharp doesn't support these formats without imagemagick or libvips installed + // so they will be stored as they are :( + if (['gif', 'x-icon', 'bmp', 'webm'].includes(emojiData.extension)) { + fileBuffer = file; + } else { + // This is to support the idea of having "sticker-like" emojis + const { data: resizedEmojiBuffer } = await Media.resizeFromBuffer(file, 512, 512, true, false, false, 'inside'); + fileBuffer = resizedEmojiBuffer; + } + + const rs = RocketChatFile.bufferToStream(fileBuffer); + await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); + + return new Promise((resolve) => { + const ws = RocketChatFileEmojiCustomInstance.createWriteStream( + encodeURIComponent(`${emojiData.name}.${emojiData.extension}`), + contentType, + ); + ws.on('end', () => { + setTimeout(() => api.broadcast('emoji.updateCustom', emojiData), 500); + resolve(); + }); + + rs.pipe(ws); + }); +} diff --git a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts index 555d5544ab339..c16d3b82449bf 100644 --- a/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/deleteEmojiCustom.ts @@ -1,13 +1,13 @@ import { api } from '@rocket.chat/core-services'; import type { ICustomEmojiDescriptor } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { EmojiCustom } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteEmojiCustom(emojiID: ICustomEmojiDescriptor['_id']): boolean; diff --git a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts index 874180097cb94..1891d1b3ed95d 100644 --- a/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts +++ b/apps/meteor/app/emoji-custom/server/methods/insertOrUpdateEmoji.ts @@ -1,15 +1,9 @@ -import { api } from '@rocket.chat/core-services'; -import { EmojiCustom } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import limax from 'limax'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import { trim } from '../../../../lib/utils/stringUtils'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; +import { insertOrUpdateEmoji } from '../lib/insertOrUpdateEmoji'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { insertOrUpdateEmoji(emojiData: { @@ -26,130 +20,12 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async insertOrUpdateEmoji(emojiData) { - if (!this.userId || !(await hasPermissionAsync(this.userId, 'manage-emoji'))) { - throw new Meteor.Error('not_authorized'); - } - - if (!trim(emojiData.name)) { - throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { - method: 'insertOrUpdateEmoji', - field: 'Name', - }); - } - - emojiData.name = limax(emojiData.name, { replacement: '_' }); - emojiData.aliases = limax(emojiData.aliases, { replacement: '_' }); - - // allow all characters except colon, whitespace, comma, >, <, &, ", ', /, \, (, ) - // more practical than allowing specific sets of characters; also allows foreign languages - const nameValidation = /[\s,:><&"'\/\\\(\)]/; - const aliasValidation = /[:><&\|"'\/\\\(\)]/; - - // silently strip colon; this allows for uploading :emojiname: as emojiname - emojiData.name = emojiData.name.replace(/:/g, ''); - emojiData.aliases = emojiData.aliases.replace(/:/g, ''); - - if (nameValidation.test(emojiData.name)) { - throw new Meteor.Error('error-input-is-not-a-valid-field', `${emojiData.name} is not a valid name`, { - method: 'insertOrUpdateEmoji', - input: emojiData.name, - field: 'Name', - }); - } - - let aliases: string[] = []; - if (emojiData.aliases) { - if (aliasValidation.test(emojiData.aliases)) { - throw new Meteor.Error('error-input-is-not-a-valid-field', `${emojiData.aliases} is not a valid alias set`, { - method: 'insertOrUpdateEmoji', - input: emojiData.aliases, - field: 'Alias_Set', - }); - } - aliases = _.without(emojiData.aliases.split(/[\s,]/).filter(Boolean), emojiData.name); - } - - emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; - - let matchingResults = []; - - if (emojiData._id) { - matchingResults = await EmojiCustom.findByNameOrAliasExceptID(emojiData.name, emojiData._id).toArray(); - for await (const alias of aliases) { - matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAliasExceptID(alias, emojiData._id).toArray()); - } - } else { - matchingResults = await EmojiCustom.findByNameOrAlias(emojiData.name).toArray(); - for await (const alias of aliases) { - matchingResults = matchingResults.concat(await EmojiCustom.findByNameOrAlias(alias).toArray()); - } - } - - if (matchingResults.length > 0) { - throw new Meteor.Error( - 'Custom_Emoji_Error_Name_Or_Alias_Already_In_Use', - 'The custom emoji or one of its aliases is already in use', - { method: 'insertOrUpdateEmoji' }, - ); - } - - if (typeof emojiData.extension === 'undefined') { - throw new Meteor.Error('error-the-field-is-required', 'The custom emoji file is required', { - method: 'insertOrUpdateEmoji', - }); - } + const emoji = await insertOrUpdateEmoji(this.userId, emojiData); if (!emojiData._id) { - // insert emoji - const createEmoji = { - name: emojiData.name, - aliases, - extension: emojiData.extension, - }; - - const _id = (await EmojiCustom.create(createEmoji)).insertedId; - - void api.broadcast('emoji.updateCustom', createEmoji); - - return _id; + return emoji._id; } - // update emoji - if (emojiData.newFile) { - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.previousExtension}`)); - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.extension}`)); - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`)); - - await EmojiCustom.setExtension(emojiData._id, emojiData.extension); - } else if (emojiData.name !== emojiData.previousName) { - const rs = await RocketChatFileEmojiCustomInstance.getFileWithReadStream( - encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`), - ); - if (rs !== null) { - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); - const ws = RocketChatFileEmojiCustomInstance.createWriteStream( - encodeURIComponent(`${emojiData.name}.${emojiData.previousExtension}`), - rs.contentType, - ); - ws.on('end', () => - RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.previousName}.${emojiData.previousExtension}`)), - ); - rs.readStream.pipe(ws); - } - } - - if (emojiData.name !== emojiData.previousName) { - await EmojiCustom.setName(emojiData._id, emojiData.name); - } - - if (emojiData.aliases) { - await EmojiCustom.setAliases(emojiData._id, aliases); - } else { - await EmojiCustom.setAliases(emojiData._id, []); - } - - void api.broadcast('emoji.updateCustom', emojiData); - return true; + return !!emoji; }, }); diff --git a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts index 485737b95f9b4..a16d536d92d45 100644 --- a/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/listEmojiCustom.ts @@ -1,12 +1,12 @@ import type { IEmojiCustom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { EmojiCustom } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { listEmojiCustom(options?: { name?: string; aliases?: string[] }): IEmojiCustom[]; diff --git a/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts b/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts index ded98b7f54695..e387888b1311c 100644 --- a/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts +++ b/apps/meteor/app/emoji-custom/server/methods/uploadEmojiCustom.ts @@ -1,22 +1,9 @@ -import { api, Media } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import limax from 'limax'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -import sharp from 'sharp'; -import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { RocketChatFile } from '../../../file/server'; -import { RocketChatFileEmojiCustomInstance } from '../startup/emoji-custom'; +import { uploadEmojiCustom } from '../lib/uploadEmojiCustom'; -const getFile = async (file: Buffer, extension: string) => { - if (extension !== 'svg+xml') { - return file; - } - - return sharp(file).png().toBuffer(); -}; - -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { uploadEmojiCustom( @@ -33,44 +20,6 @@ declare module '@rocket.chat/ui-contexts' { Meteor.methods({ async uploadEmojiCustom(binaryContent, contentType, emojiData) { - // technically, since this method doesnt have any datatype validations, users can - // upload videos as emojis. The FE won't play them, but they will waste space for sure. - if (!this.userId || !(await hasPermissionAsync(this.userId, 'manage-emoji'))) { - throw new Meteor.Error('not_authorized'); - } - - emojiData.name = limax(emojiData.name, { replacement: '_' }); - // delete aliases for notification purposes. here, it is a string rather than an array - delete emojiData.aliases; - - const file = await getFile(Buffer.from(binaryContent, 'binary'), emojiData.extension); - emojiData.extension = emojiData.extension === 'svg+xml' ? 'png' : emojiData.extension; - - let fileBuffer; - // sharp doesn't support these formats without imagemagick or libvips installed - // so they will be stored as they are :( - if (['gif', 'x-icon', 'bmp', 'webm'].includes(emojiData.extension)) { - fileBuffer = file; - } else { - // This is to support the idea of having "sticker-like" emojis - const { data: resizedEmojiBuffer } = await Media.resizeFromBuffer(file, 512, 512, true, false, false, 'inside'); - fileBuffer = resizedEmojiBuffer; - } - - const rs = RocketChatFile.bufferToStream(fileBuffer); - await RocketChatFileEmojiCustomInstance.deleteFile(encodeURIComponent(`${emojiData.name}.${emojiData.extension}`)); - - return new Promise((resolve) => { - const ws = RocketChatFileEmojiCustomInstance.createWriteStream( - encodeURIComponent(`${emojiData.name}.${emojiData.extension}`), - contentType, - ); - ws.on('end', () => { - setTimeout(() => api.broadcast('emoji.updateCustom', emojiData), 500); - resolve(); - }); - - rs.pipe(ws); - }); + await uploadEmojiCustom(this.userId, binaryContent, contentType, emojiData); }, }); diff --git a/apps/meteor/app/federation/server/endpoints/dispatch.js b/apps/meteor/app/federation/server/endpoints/dispatch.js index 4cab0b0c41e83..6c7fa5cad0e84 100644 --- a/apps/meteor/app/federation/server/endpoints/dispatch.js +++ b/apps/meteor/app/federation/server/endpoints/dispatch.js @@ -3,11 +3,10 @@ import { eventTypes } from '@rocket.chat/core-typings'; import { FederationServers, FederationRoomEvents, Rooms, Messages, Subscriptions, Users, ReadReceipts } from '@rocket.chat/models'; import EJSON from 'ejson'; -import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { API } from '../../../api/server'; import { FileUpload } from '../../../file-upload/server'; import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; -import { notifyOnRoomChanged, notifyOnRoomChangedById } from '../../../lib/server/lib/notifyListener'; +import { notifyOnMessageChange, notifyOnRoomChanged, notifyOnRoomChangedById } from '../../../lib/server/lib/notifyListener'; import { notifyUsersOnMessage } from '../../../lib/server/lib/notifyUsersOnMessage'; import { sendAllNotifications } from '../../../lib/server/lib/sendNotificationsOnMessage'; import { processThreads } from '../../../threads/server/hooks/aftersavemessage'; @@ -303,7 +302,7 @@ const eventHandlers = { } } if (messageForNotification) { - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: messageForNotification._id, data: messageForNotification, }); @@ -334,7 +333,7 @@ const eventHandlers = { } else { // Update the message await Messages.updateOne({ _id: persistedMessage._id }, { $set: { msg: message.msg, federation: message.federation } }); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: persistedMessage._id, data: { ...persistedMessage, @@ -404,7 +403,7 @@ const eventHandlers = { // Update the property await Messages.updateOne({ _id: messageId }, { $set: { [`reactions.${reaction}`]: reactionObj } }); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: persistedMessage._id, data: { ...persistedMessage, @@ -462,7 +461,7 @@ const eventHandlers = { // Otherwise, update the property await Messages.updateOne({ _id: messageId }, { $set: { [`reactions.${reaction}`]: reactionObj } }); } - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: persistedMessage._id, data: { ...persistedMessage, diff --git a/apps/meteor/app/federation/server/methods/dashboard.ts b/apps/meteor/app/federation/server/methods/dashboard.ts index 90a050556893a..eacb42b24cbb2 100644 --- a/apps/meteor/app/federation/server/methods/dashboard.ts +++ b/apps/meteor/app/federation/server/methods/dashboard.ts @@ -1,10 +1,10 @@ import type { IFederationServer } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { federationGetServers, federationGetOverviewData } from '../functions/dashboard'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'federation:getServers': () => { data: IFederationServer[] }; diff --git a/apps/meteor/app/federation/server/methods/loadContextEvents.ts b/apps/meteor/app/federation/server/methods/loadContextEvents.ts index 9e286bbf913ac..e20acb7e37c99 100644 --- a/apps/meteor/app/federation/server/methods/loadContextEvents.ts +++ b/apps/meteor/app/federation/server/methods/loadContextEvents.ts @@ -1,11 +1,11 @@ import type { IFederationEvent } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { FederationRoomEvents } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'federation:loadContextEvents'(latestEventTimestamp: number): IFederationEvent[]; diff --git a/apps/meteor/app/federation/server/methods/testSetup.ts b/apps/meteor/app/federation/server/methods/testSetup.ts index 93e5015354747..bc50ef1581944 100644 --- a/apps/meteor/app/federation/server/methods/testSetup.ts +++ b/apps/meteor/app/federation/server/methods/testSetup.ts @@ -1,11 +1,11 @@ import { eventTypes } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { dispatchEvent } from '../handler'; import { getFederationDomain } from '../lib/getFederationDomain'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { FEDERATION_Test_Setup(): { message: string }; diff --git a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts index fdb8d131916d7..3e9bcd1926744 100644 --- a/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts +++ b/apps/meteor/app/file-upload/server/methods/getS3FileUrl.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Uploads } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -7,7 +7,7 @@ import { UploadFS } from '../../../../server/ufs'; import { canAccessRoomAsync } from '../../../authorization/server'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getS3FileUrl(fileId: string): string; diff --git a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts index 485528a5e62fe..73dfd0216a732 100644 --- a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts +++ b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts @@ -7,8 +7,8 @@ import type { FilesAndAttachments, IMessage, } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms, Uploads, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -141,7 +141,7 @@ export const parseFileIntoMessageAttachments = async ( return { files, attachments }; }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { sendFileMessage: (roomId: string, _store: string, file: Partial, msgData?: Record) => boolean; diff --git a/apps/meteor/app/iframe-login/server/iframe_server.ts b/apps/meteor/app/iframe-login/server/iframe_server.ts index c7e67edf9deb1..b3e661cf6ff33 100644 --- a/apps/meteor/app/iframe-login/server/iframe_server.ts +++ b/apps/meteor/app/iframe-login/server/iframe_server.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Accounts } from 'meteor/accounts-base'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -23,7 +23,7 @@ Accounts.registerLoginHandler('iframe', async (result) => { } }); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'OAuth.retrieveCredential'(credentialToken: string, credentialSecret: string): unknown; diff --git a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts index 81e06ec8eb0f2..d2da85c7cfd49 100644 --- a/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts +++ b/apps/meteor/app/importer/server/methods/downloadPublicImportFile.ts @@ -4,7 +4,7 @@ import https from 'https'; import { Import } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { Importers } from '..'; @@ -75,7 +75,7 @@ export const executeDownloadPublicImportFile = async (userId: IUser['_id'], file } }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { downloadPublicImportFile(fileUrl: string, importerKey: string): void; diff --git a/apps/meteor/app/importer/server/methods/getImportFileData.ts b/apps/meteor/app/importer/server/methods/getImportFileData.ts index 03f9a53abe6c2..1d36f7fc5a5ee 100644 --- a/apps/meteor/app/importer/server/methods/getImportFileData.ts +++ b/apps/meteor/app/importer/server/methods/getImportFileData.ts @@ -2,8 +2,8 @@ import fs from 'fs'; import path from 'path'; import type { IImportProgress, IImporterSelection } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Imports } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { Importers } from '..'; @@ -61,7 +61,7 @@ export const executeGetImportFileData = async (): Promise => { return instance.getProgress(); }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getImportProgress(): IImportProgress; diff --git a/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts b/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts index edf021a19eac7..0471cb57955d5 100644 --- a/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts +++ b/apps/meteor/app/importer/server/methods/getLatestImportOperations.ts @@ -1,6 +1,6 @@ import type { IImport } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Imports } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -17,7 +17,7 @@ export const executeGetLatestImportOperations = async () => { return data.toArray(); }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getLatestImportOperations(): IImport[]; diff --git a/apps/meteor/app/importer/server/methods/startImport.ts b/apps/meteor/app/importer/server/methods/startImport.ts index af91295ede292..bbb5ce76ad1ce 100644 --- a/apps/meteor/app/importer/server/methods/startImport.ts +++ b/apps/meteor/app/importer/server/methods/startImport.ts @@ -1,7 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Imports } from '@rocket.chat/models'; import type { StartImportParamsPOST } from '@rocket.chat/rest-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { Importers, Selection, SelectionChannel, SelectionUser } from '..'; @@ -32,7 +32,7 @@ export const executeStartImport = async ({ input }: StartImportParamsPOST, start await instance.startImport(selection, startedByUserId); }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { startImport(params: StartImportParamsPOST): void; diff --git a/apps/meteor/app/importer/server/methods/uploadImportFile.ts b/apps/meteor/app/importer/server/methods/uploadImportFile.ts index d6ded455793b2..df5d2af883fae 100644 --- a/apps/meteor/app/importer/server/methods/uploadImportFile.ts +++ b/apps/meteor/app/importer/server/methods/uploadImportFile.ts @@ -1,6 +1,6 @@ import { Import } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { Importers } from '..'; @@ -55,7 +55,7 @@ export const executeUploadImportFile = async ( await instance.updateProgress(ProgressStep.FILE_LOADED); }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { uploadImportFile(binaryContent: string, contentType: string, fileName: string, importerKey: string): void; diff --git a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts index 5b8f13ef1a3ad..a30bb9b5ee9a3 100644 --- a/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts +++ b/apps/meteor/app/integrations/server/methods/clearIntegrationHistory.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import notifications from '../../../notifications/server/lib/Notifications'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { clearIntegrationHistory(integrationId: string): Promise; diff --git a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts index db058bec960bb..8f1f1b5dd5760 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts @@ -1,7 +1,7 @@ import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Babel } from 'meteor/babel-compiler'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -13,7 +13,7 @@ import { validateScriptEngine, isScriptEngineFrozen } from '../../lib/validateSc const validChannelChars = ['@', '#']; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addIncomingIntegration(integration: INewIncomingIntegration): Promise; diff --git a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts index e73a46bb27db8..9a116f51bbafa 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/deleteIncomingIntegration.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { notifyOnIntegrationChangedById } from '../../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteIncomingIntegration(integrationId: string): Promise; diff --git a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts index 0ea5028130da0..c94b7b9fd7a88 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts @@ -1,7 +1,7 @@ import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { wrapExceptions } from '@rocket.chat/tools'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Babel } from 'meteor/babel-compiler'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; @@ -12,7 +12,7 @@ import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateSc const validChannelChars = ['@', '#']; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { updateIncomingIntegration( diff --git a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts index c8dc31e084466..dc54c76b1897f 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/addOutgoingIntegration.ts @@ -1,6 +1,6 @@ import type { INewOutgoingIntegration, IOutgoingIntegration } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyLis import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; import { validateScriptEngine } from '../../lib/validateScriptEngine'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addOutgoingIntegration(integration: INewOutgoingIntegration): Promise; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts index c9f2211d835b5..46d4c65159c07 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/deleteOutgoingIntegration.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { notifyOnIntegrationChangedById } from '../../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteOutgoingIntegration(integrationId: string): Promise; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts index 417c308ca6cb0..83c13f2d981ba 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/replayOutgoingIntegration.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, IntegrationHistory } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { triggerHandler } from '../../lib/triggerHandler'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { replayOutgoingIntegration(params: { integrationId: string; historyId: string }): Promise; diff --git a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts index 116dbd043039c..c9b1f4ed7d647 100644 --- a/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/outgoing/updateOutgoingIntegration.ts @@ -1,7 +1,7 @@ import type { IIntegration, INewOutgoingIntegration, IUpdateOutgoingIntegration } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Integrations, Users } from '@rocket.chat/models'; import { wrapExceptions } from '@rocket.chat/tools'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; @@ -9,7 +9,7 @@ import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyLis import { validateOutgoingIntegration } from '../../lib/validateOutgoingIntegration'; import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { updateOutgoingIntegration( diff --git a/apps/meteor/app/irc/server/methods/resetIrcConnection.ts b/apps/meteor/app/irc/server/methods/resetIrcConnection.ts index 24eef975d5d54..aaaeef1c06b8c 100644 --- a/apps/meteor/app/irc/server/methods/resetIrcConnection.ts +++ b/apps/meteor/app/irc/server/methods/resetIrcConnection.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; import { settings } from '../../../settings/server'; import Bridge from '../irc-bridge'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { resetIrcConnection(): { message: string; params: unknown[] }; diff --git a/apps/meteor/app/lib/client/methods/sendMessage.ts b/apps/meteor/app/lib/client/methods/sendMessage.ts index e824c9e491afc..bdaca587493af 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.ts +++ b/apps/meteor/app/lib/client/methods/sendMessage.ts @@ -1,5 +1,5 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { onClientMessageReceived } from '../../../../client/lib/onClientMessageReceived'; diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 57ea20f00cb1e..b6ffc0ca4629a 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -15,9 +15,15 @@ import { notifyOnRoomChangedById } from '../lib/notifyListener'; export const addUserToRoom = async function ( rid: string, - user: Pick | string, + user: Pick | string, inviter?: Pick, - silenced?: boolean, + { + skipSystemMessage, + skipAlertSound, + }: { + skipSystemMessage?: boolean; + skipAlertSound?: boolean; + } = {}, ): Promise { const now = new Date(); const room = await Rooms.findOneById(rid); @@ -43,12 +49,12 @@ export const addUserToRoom = async function ( } try { - await callbacks.run('federation.beforeAddUserToARoom', { user, inviter }, room); + await callbacks.run('federation.beforeAddUserToARoom', { user: userToBeAdded, inviter }, room); } catch (error) { throw new Meteor.Error((error as any)?.message); } - await callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter: userToBeAdded }); + await callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter }); // Check if user is already in room const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, userToBeAdded._id); @@ -79,7 +85,7 @@ export const addUserToRoom = async function ( await Subscriptions.createWithRoomAndUser(room, userToBeAdded as IUser, { ts: now, open: true, - alert: true, + alert: !skipAlertSound, unread: 1, userMentions: 1, groupMentions: 0, @@ -93,7 +99,7 @@ export const addUserToRoom = async function ( throw new Meteor.Error('error-invalid-user', 'Cannot add an user to a room without a username'); } - if (!silenced) { + if (!skipSystemMessage) { if (inviter) { const extraData = { ts: now, diff --git a/apps/meteor/app/lib/server/functions/checkUrlForSsrf.ts b/apps/meteor/app/lib/server/functions/checkUrlForSsrf.ts new file mode 100644 index 0000000000000..c90065d7ad8f3 --- /dev/null +++ b/apps/meteor/app/lib/server/functions/checkUrlForSsrf.ts @@ -0,0 +1,100 @@ +import { lookup } from 'dns'; + +// https://en.wikipedia.org/wiki/Reserved_IP_addresses + Alibaba Metadata IP +const ranges: string[] = [ + '0.0.0.0/8', + '10.0.0.0/8', + '100.64.0.0/10', + '127.0.0.0/8', + '169.254.0.0/16', + '172.16.0.0/12', + '192.0.0.0/24', + '192.0.2.0/24', + '192.88.99.0/24', + '192.168.0.0/16', + '198.18.0.0/15', + '198.51.100.0/24', + '203.0.113.0/24', + '224.0.0.0/4', + '240.0.0.0/4', + '255.255.255.255', + '100.100.100.200/32', +]; + +export const nslookup = async (hostname: string): Promise => { + return new Promise((resolve, reject) => { + lookup(hostname, (error, address) => { + if (error) { + reject(error); + } else { + resolve(address); + } + }); + }); +}; + +export const ipToLong = (ip: string): number => { + return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0; +}; + +export const isIpInRange = (ip: string, range: string): boolean => { + const [rangeIp, subnet] = range.split('/'); + const ipLong = ipToLong(ip); + const rangeIpLong = ipToLong(rangeIp); + const mask = ~(2 ** (32 - Number(subnet)) - 1); + return (ipLong & mask) === (rangeIpLong & mask); +}; + +export const isIpInAnyRange = (ip: string): boolean => ranges.some((range) => isIpInRange(ip, range)); + +export const isValidIPv4 = (ip: string): boolean => { + const octets = ip.split('.'); + if (octets.length !== 4) return false; + return octets.every((octet) => { + const num = Number(octet); + return num >= 0 && num <= 255 && octet === num.toString(); + }); +}; + +export const isValidDomain = (domain: string): boolean => { + const domainPattern = /^(?!-)(?!.*--)[A-Za-z0-9-]{1,63}(? => { + if (!(url.startsWith('http://') || url.startsWith('https://'))) { + return false; + } + + const [, address] = url.split('://'); + const ipOrDomain = address.includes('/') ? address.split('/')[0] : address; + + if (!(isValidIPv4(ipOrDomain) || isValidDomain(ipOrDomain))) { + return false; + } + + if (isValidIPv4(ipOrDomain) && isIpInAnyRange(ipOrDomain)) { + return false; + } + + if (isValidDomain(ipOrDomain) && /metadata.google.internal/.test(ipOrDomain.toLowerCase())) { + return false; + } + + if (isValidDomain(ipOrDomain)) { + try { + const ipAddress = await nslookup(ipOrDomain); + if (isIpInAnyRange(ipAddress)) { + return false; + } + } catch (error) { + console.log(error); + return false; + } + } + + return true; +}; diff --git a/apps/meteor/app/lib/server/functions/closeLivechatRoom.ts b/apps/meteor/app/lib/server/functions/closeLivechatRoom.ts new file mode 100644 index 0000000000000..b716be044d578 --- /dev/null +++ b/apps/meteor/app/lib/server/functions/closeLivechatRoom.ts @@ -0,0 +1,81 @@ +import type { IUser, IRoom, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms, Subscriptions } from '@rocket.chat/models'; + +import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import type { CloseRoomParams } from '../../../livechat/server/lib/LivechatTyped'; +import { Livechat } from '../../../livechat/server/lib/LivechatTyped'; + +export const closeLivechatRoom = async ( + user: IUser, + roomId: IRoom['_id'], + { + comment, + tags, + generateTranscriptPdf, + transcriptEmail, + }: { + comment?: string; + tags?: string[]; + generateTranscriptPdf?: boolean; + transcriptEmail?: + | { + sendToVisitor: false; + } + | { + sendToVisitor: true; + requestData: Pick, 'email' | 'subject'>; + }; + }, +): Promise => { + const room = await LivechatRooms.findOneById(roomId); + if (!room || !isOmnichannelRoom(room)) { + throw new Error('error-invalid-room'); + } + + if (!room.open) { + const subscriptionsLeft = await Subscriptions.countByRoomId(roomId); + if (subscriptionsLeft) { + await Subscriptions.removeByRoomId(roomId); + return; + } + throw new Error('error-room-already-closed'); + } + + const subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, user._id, { projection: { _id: 1 } }); + if (!subscription && !(await hasPermissionAsync(user._id, 'close-others-livechat-room'))) { + throw new Error('error-not-authorized'); + } + + const options: CloseRoomParams['options'] = { + clientAction: true, + tags, + ...(generateTranscriptPdf && { pdfTranscript: { requestedBy: user._id } }), + ...(transcriptEmail && { + ...(transcriptEmail.sendToVisitor + ? { + emailTranscript: { + sendToVisitor: true, + requestData: { + email: transcriptEmail.requestData.email, + subject: transcriptEmail.requestData.subject, + requestedAt: new Date(), + requestedBy: user, + }, + }, + } + : { + emailTranscript: { + sendToVisitor: false, + }, + }), + }), + }; + + await Livechat.closeRoom({ + room, + user, + options, + comment, + }); +}; diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 19e5fb2f94896..183cb789051f1 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -112,6 +112,7 @@ export const createRoom = async ( readOnly?: boolean, roomExtraData?: Partial, options?: ICreateRoomParams['options'], + sidepanel?: ICreateRoomParams['sidepanel'], ): Promise< ICreatedRoom & { rid: string; @@ -187,6 +188,7 @@ export const createRoom = async ( }, ts: now, ro: readOnly === true, + sidepanel, }; if (teamId) { diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index e977874b34540..04542d5f1d27f 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -5,11 +5,10 @@ import { Messages, Rooms, Uploads, Users, ReadReceipts } from '@rocket.chat/mode import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { canDeleteMessageAsync } from '../../../authorization/server/functions/canDeleteMessage'; import { FileUpload } from '../../../file-upload/server'; import { settings } from '../../../settings/server'; -import { notifyOnRoomChangedById } from '../lib/notifyListener'; +import { notifyOnRoomChangedById, notifyOnMessageChange } from '../lib/notifyListener'; export const deleteMessageValidatingPermission = async (message: AtLeast, userId: IUser['_id']): Promise => { if (!message?._id) { @@ -93,7 +92,7 @@ export async function deleteMessage(message: IMessage, user: IUser): Promise => { if (service === 'url' && typeof dataURI === 'string') { let response: Response; + + const isSsrfSafe = await checkUrlForSsrf(dataURI); + if (!isSsrfSafe) { + throw new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${encodeURI(dataURI)}`, { + function: 'setUserAvatar', + url: dataURI, + }); + } + try { - response = await fetch(dataURI); + response = await fetch(dataURI, { redirect: 'error' }); } catch (e) { SystemLogger.info(`Not a valid response, from the avatar url: ${encodeURI(dataURI)}`); throw new Meteor.Error('error-avatar-invalid-url', `Invalid avatar URL: ${encodeURI(dataURI)}`, { diff --git a/apps/meteor/app/lib/server/functions/updateMessage.ts b/apps/meteor/app/lib/server/functions/updateMessage.ts index 2954517fb0184..b0f2acd1f4ee8 100644 --- a/apps/meteor/app/lib/server/functions/updateMessage.ts +++ b/apps/meteor/app/lib/server/functions/updateMessage.ts @@ -5,14 +5,13 @@ import { Messages, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; import { settings } from '../../../settings/server'; -import { notifyOnRoomChangedById } from '../lib/notifyListener'; +import { notifyOnRoomChangedById, notifyOnMessageChange } from '../lib/notifyListener'; import { validateCustomMessageFields } from '../lib/validateCustomMessageFields'; import { parseUrlsInMessage } from './parseUrlsInMessage'; export const updateMessage = async function ( - message: AtLeast, + message: AtLeast, user: IUser, originalMsg?: IMessage, previewUrls?: string[], @@ -102,7 +101,7 @@ export const updateMessage = async function ( // so we wait for it to run before broadcasting const data = await callbacks.run('afterSaveMessage', msg, room, user._id); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: msg._id, data: data as any, // TODO move "afterSaveMessage" type definition to specify a return value }); diff --git a/apps/meteor/app/lib/server/functions/validateNameChars.ts b/apps/meteor/app/lib/server/functions/validateNameChars.ts new file mode 100644 index 0000000000000..07330c66b7628 --- /dev/null +++ b/apps/meteor/app/lib/server/functions/validateNameChars.ts @@ -0,0 +1,21 @@ +export const validateNameChars = (name: string | undefined): boolean => { + if (typeof name !== 'string') { + return false; + } + + const invalidChars = /[<>\\/]/; + if (invalidChars.test(name)) { + return false; + } + + try { + const decodedName = decodeURI(name); + if (invalidChars.test(decodedName)) { + return false; + } + } catch (err) { + return false; + } + + return true; +}; diff --git a/apps/meteor/app/lib/server/lib/notifyListener.ts b/apps/meteor/app/lib/server/lib/notifyListener.ts index 635c236fda27a..83ab5774374a6 100644 --- a/apps/meteor/app/lib/server/lib/notifyListener.ts +++ b/apps/meteor/app/lib/server/lib/notifyListener.ts @@ -17,6 +17,9 @@ import type { AtLeast, ISettingColor, IUser, + IMessage, + SettingValue, + MessageTypesValues, } from '@rocket.chat/core-typings'; import { Rooms, @@ -30,7 +33,11 @@ import { LivechatInquiry, LivechatDepartmentAgents, Users, + Messages, } from '@rocket.chat/models'; +import mem from 'mem'; + +import { shouldHideSystemMessage } from '../../../../server/lib/systemMessage/hideSystemMessage'; type ClientAction = 'inserted' | 'updated' | 'removed'; @@ -401,3 +408,65 @@ export const notifyOnUserChangeById = withDbWatcherCheck( void notifyOnUserChange({ id, clientAction, data: user }); }, ); + +const getUserNameCached = mem( + async (userId: string): Promise => { + const user = await Users.findOne>(userId, { projection: { name: 1 } }); + return user?.name; + }, + { maxAge: 10000 }, +); + +const getSettingCached = mem(async (setting: string): Promise => Settings.getValueById(setting), { maxAge: 10000 }); + +export async function getMessageToBroadcast({ id, data }: { id: IMessage['_id']; data?: IMessage }): Promise { + const message = data ?? (await Messages.findOneById(id)); + if (!message) { + return; + } + + if (message.t) { + const hiddenSystemMessages = (await getSettingCached('Hide_System_Messages')) as MessageTypesValues[]; + const shouldHide = shouldHideSystemMessage(message.t, hiddenSystemMessages); + + if (shouldHide) { + return; + } + } + + if (message._hidden || message.imported != null) { + return; + } + + const useRealName = (await getSettingCached('UI_Use_Real_Name')) === true; + if (useRealName) { + if (message.u?._id) { + const name = await getUserNameCached(message.u._id); + if (name) { + message.u.name = name; + } + } + + if (message.mentions?.length) { + for await (const mention of message.mentions) { + const name = await getUserNameCached(mention._id); + if (name) { + mention.name = name; + } + } + } + } + + return message; +} + +export const notifyOnMessageChange = withDbWatcherCheck(async ({ id, data }: { id: IMessage['_id']; data?: IMessage }): Promise => { + if (!dbWatchersDisabled) { + return; + } + const message = await getMessageToBroadcast({ id, data }); + if (!message) { + return; + } + void api.broadcast('watch.messages', { message }); +}); diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts index 44654428ae8fb..49fcc0ea4725c 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts @@ -266,7 +266,7 @@ export async function sendMessageNotifications(message: IMessage, room: IRoom, u return; } - const sender = await roomCoordinator.getRoomDirectives(room.t).getMsgSender(message.u._id); + const sender = await roomCoordinator.getRoomDirectives(room.t).getMsgSender(message); if (!sender) { return message; } diff --git a/apps/meteor/app/lib/server/methods/addOAuthService.ts b/apps/meteor/app/lib/server/methods/addOAuthService.ts index 05b0e5a7e4e60..09287bb9bb746 100644 --- a/apps/meteor/app/lib/server/methods/addOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/addOAuthService.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { addOAuthService } from '../../../../server/lib/oauth/addOAuthService'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addOAuthService(name: string): void; diff --git a/apps/meteor/app/lib/server/methods/addUserToRoom.ts b/apps/meteor/app/lib/server/methods/addUserToRoom.ts index 880b413d06182..8100cafc33b00 100644 --- a/apps/meteor/app/lib/server/methods/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/methods/addUserToRoom.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { addUsersToRoomMethod } from './addUsersToRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addUserToRoom(data: { rid: string; username: string }): void; diff --git a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts index 48b7f9db58f51..73fbf6e51a04b 100644 --- a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts +++ b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts @@ -1,8 +1,8 @@ import { api } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -12,7 +12,7 @@ import { Federation } from '../../../../server/services/federation/Federation'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { addUserToRoom } from '../functions/addUserToRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addUsersToRoom(data: { rid: string; users: string[] }): boolean; diff --git a/apps/meteor/app/lib/server/methods/archiveRoom.ts b/apps/meteor/app/lib/server/methods/archiveRoom.ts index c30014f59c11f..dfa5a2c55412d 100644 --- a/apps/meteor/app/lib/server/methods/archiveRoom.ts +++ b/apps/meteor/app/lib/server/methods/archiveRoom.ts @@ -1,6 +1,6 @@ import { isRegisterUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { archiveRoom } from '../functions/archiveRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { archiveRoom(rid: string): Promise; diff --git a/apps/meteor/app/lib/server/methods/blockUser.ts b/apps/meteor/app/lib/server/methods/blockUser.ts index b65423edf25bf..b967e35d7bc19 100644 --- a/apps/meteor/app/lib/server/methods/blockUser.ts +++ b/apps/meteor/app/lib/server/methods/blockUser.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { blockUser({ rid, blocked }: { rid: string; blocked: string }): boolean; diff --git a/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts b/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts index 3f349a8b43e67..ecba6844da262 100644 --- a/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts +++ b/apps/meteor/app/lib/server/methods/checkRegistrationSecretURL.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; diff --git a/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts b/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts index 34ad02ddb2cf2..4df2706bb4796 100644 --- a/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts +++ b/apps/meteor/app/lib/server/methods/checkUsernameAvailability.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +6,7 @@ import { checkUsernameAvailabilityWithValidation } from '../functions/checkUsern import { RateLimiter } from '../lib'; import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { checkUsernameAvailability(username: string): boolean; diff --git a/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts b/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts index a724d1b38b2c7..d6136eee91315 100644 --- a/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts +++ b/apps/meteor/app/lib/server/methods/cleanRoomHistory.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { cleanRoomHistory } from '../functions/cleanRoomHistory'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { cleanRoomHistory(data: { diff --git a/apps/meteor/app/lib/server/methods/createChannel.ts b/apps/meteor/app/lib/server/methods/createChannel.ts index 98cea517bed4b..a490fa1e3c424 100644 --- a/apps/meteor/app/lib/server/methods/createChannel.ts +++ b/apps/meteor/app/lib/server/methods/createChannel.ts @@ -1,13 +1,13 @@ import type { ICreatedRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { createRoom } from '../functions/createRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { createChannel( diff --git a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts index 75097b5c89b88..3b92b26078297 100644 --- a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts +++ b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts @@ -1,13 +1,13 @@ import type { ICreatedRoom, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { createRoom } from '../functions/createRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { createPrivateGroup( diff --git a/apps/meteor/app/lib/server/methods/createToken.ts b/apps/meteor/app/lib/server/methods/createToken.ts index ed665944415ad..de60a7e37fe8d 100644 --- a/apps/meteor/app/lib/server/methods/createToken.ts +++ b/apps/meteor/app/lib/server/methods/createToken.ts @@ -1,12 +1,12 @@ import { User } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { createToken(userId: string): { userId: string; authToken: string }; diff --git a/apps/meteor/app/lib/server/methods/deleteMessage.ts b/apps/meteor/app/lib/server/methods/deleteMessage.ts index a1b1000c06b87..b0be64c245ef6 100644 --- a/apps/meteor/app/lib/server/methods/deleteMessage.ts +++ b/apps/meteor/app/lib/server/methods/deleteMessage.ts @@ -1,11 +1,11 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { deleteMessageValidatingPermission } from '../functions/deleteMessage'; import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteMessage({ _id }: Pick): void; diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index 2d651950da190..7b5663185bc56 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -1,7 +1,7 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; import { SHA256 } from '@rocket.chat/sha256'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Accounts } from 'meteor/accounts-base'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -10,7 +10,7 @@ import { trim } from '../../../../lib/utils/stringUtils'; import { settings } from '../../../settings/server'; import { deleteUser } from '../functions/deleteUser'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteUserOwnAccount(password: string, confirmRelinquish?: boolean): Promise; diff --git a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts index 80b5428efbef1..1c116863610dd 100644 --- a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts +++ b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts @@ -1,10 +1,10 @@ import type { IMessage, RequiredField, SlashCommandPreviewItem } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { slashCommands } from '../../../utils/server/slashCommand'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { executeSlashCommandPreview( diff --git a/apps/meteor/app/lib/server/methods/getChannelHistory.ts b/apps/meteor/app/lib/server/methods/getChannelHistory.ts index 00ff016395931..8f1f4c5861418 100644 --- a/apps/meteor/app/lib/server/methods/getChannelHistory.ts +++ b/apps/meteor/app/lib/server/methods/getChannelHistory.ts @@ -1,6 +1,6 @@ import type { IMessage, MessageTypesValues } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; @@ -11,7 +11,7 @@ import { settings } from '../../../settings/server/cached'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { getHiddenSystemMessages } from '../lib/getHiddenSystemMessages'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getChannelHistory(params: { diff --git a/apps/meteor/app/lib/server/methods/getMessages.ts b/apps/meteor/app/lib/server/methods/getMessages.ts index 909e3d0c656b6..d8684f82453ce 100644 --- a/apps/meteor/app/lib/server/methods/getMessages.ts +++ b/apps/meteor/app/lib/server/methods/getMessages.ts @@ -1,12 +1,12 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getMessages(messages: IMessage['_id'][]): Promise; diff --git a/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts b/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts index 75e4a4372f57a..5ba4b34907226 100644 --- a/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts +++ b/apps/meteor/app/lib/server/methods/getRoomJoinCode.ts @@ -1,12 +1,12 @@ import { isRoomWithJoinCode } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getRoomJoinCode(rid: string): string | false; diff --git a/apps/meteor/app/lib/server/methods/getRoomRoles.ts b/apps/meteor/app/lib/server/methods/getRoomRoles.ts index ac5fcc734d0f0..b411ba8fedf53 100644 --- a/apps/meteor/app/lib/server/methods/getRoomRoles.ts +++ b/apps/meteor/app/lib/server/methods/getRoomRoles.ts @@ -1,6 +1,6 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -8,7 +8,7 @@ import { getRoomRoles } from '../../../../server/lib/roles/getRoomRoles'; import { canAccessRoomAsync } from '../../../authorization/server'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getRoomRoles(rid: IRoom['_id']): ISubscription[]; diff --git a/apps/meteor/app/lib/server/methods/getSingleMessage.ts b/apps/meteor/app/lib/server/methods/getSingleMessage.ts index 1aaf2a6522573..c4b6f065296b7 100644 --- a/apps/meteor/app/lib/server/methods/getSingleMessage.ts +++ b/apps/meteor/app/lib/server/methods/getSingleMessage.ts @@ -1,12 +1,12 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getSingleMessage(mid: IMessage['_id']): Promise; diff --git a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts index dcb39c466063c..965c91cc502fc 100644 --- a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts +++ b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts @@ -1,10 +1,10 @@ import type { IMessage, RequiredField, SlashCommandPreviews } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { slashCommands } from '../../../utils/server/slashCommand'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getSlashCommandPreviews(command: { diff --git a/apps/meteor/app/lib/server/methods/getUserRoles.ts b/apps/meteor/app/lib/server/methods/getUserRoles.ts index cc7588276c336..cbd4712930a28 100644 --- a/apps/meteor/app/lib/server/methods/getUserRoles.ts +++ b/apps/meteor/app/lib/server/methods/getUserRoles.ts @@ -1,9 +1,9 @@ import { Authorization } from '@rocket.chat/core-services'; import type { IUser, IRocketChatRecord } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getUserRoles(): (IRocketChatRecord & Pick)[]; diff --git a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts index 5724449a57904..b42799f240813 100644 --- a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts +++ b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { generateUsernameSuggestion } from '../functions/getUsernameSuggestion'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getUsernameSuggestion(): Promise; diff --git a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts index 5a8b1c8d1a584..122b11172d57a 100644 --- a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts +++ b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +6,7 @@ import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; import { saveUser } from '../functions/saveUser'; import { methodDeprecationLogger } from '../lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { insertOrUpdateUser(userData: Record): Promise; diff --git a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts index 309ac4e449be4..df654bc4aef0c 100644 --- a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts +++ b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts @@ -1,11 +1,11 @@ import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { addUserToDefaultChannels } from '../functions/addUserToDefaultChannels'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { joinDefaultChannels(silenced?: boolean): void; diff --git a/apps/meteor/app/lib/server/methods/joinRoom.ts b/apps/meteor/app/lib/server/methods/joinRoom.ts index 0fa3ac0b3c3b4..ef3f069ee6142 100644 --- a/apps/meteor/app/lib/server/methods/joinRoom.ts +++ b/apps/meteor/app/lib/server/methods/joinRoom.ts @@ -1,11 +1,11 @@ import { Room } from '@rocket.chat/core-services'; import type { IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { joinRoom(rid: IRoom['_id'], code?: string): boolean | undefined; diff --git a/apps/meteor/app/lib/server/methods/leaveRoom.ts b/apps/meteor/app/lib/server/methods/leaveRoom.ts index ec1bb1638a211..4fc85b35fd05c 100644 --- a/apps/meteor/app/lib/server/methods/leaveRoom.ts +++ b/apps/meteor/app/lib/server/methods/leaveRoom.ts @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles, Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -10,7 +10,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { removeUserFromRoom } from '../functions/removeUserFromRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { leaveRoom(rid: string): Promise; diff --git a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts index e5b1c377a33e7..1d547b7703615 100644 --- a/apps/meteor/app/lib/server/methods/refreshOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/refreshOAuthService.ts @@ -1,10 +1,10 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { refreshLoginServices } from '../../../../server/lib/refreshLoginServices'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { refreshOAuthService(): Promise; diff --git a/apps/meteor/app/lib/server/methods/removeOAuthService.ts b/apps/meteor/app/lib/server/methods/removeOAuthService.ts index 6e16dc8d2d5b5..fee2caa911d22 100644 --- a/apps/meteor/app/lib/server/methods/removeOAuthService.ts +++ b/apps/meteor/app/lib/server/methods/removeOAuthService.ts @@ -1,13 +1,13 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; import { capitalize } from '@rocket.chat/string-helpers'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { notifyOnSettingChangedById } from '../lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { removeOAuthService(name: string): Promise; diff --git a/apps/meteor/app/lib/server/methods/restartServer.ts b/apps/meteor/app/lib/server/methods/restartServer.ts index 206824aebe7ed..264ab4d1bee6d 100644 --- a/apps/meteor/app/lib/server/methods/restartServer.ts +++ b/apps/meteor/app/lib/server/methods/restartServer.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { restart_server(): { diff --git a/apps/meteor/app/lib/server/methods/saveCustomFields.ts b/apps/meteor/app/lib/server/methods/saveCustomFields.ts index a509c97bdb854..d683e59a905cc 100644 --- a/apps/meteor/app/lib/server/methods/saveCustomFields.ts +++ b/apps/meteor/app/lib/server/methods/saveCustomFields.ts @@ -1,11 +1,11 @@ import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { saveCustomFields } from '../functions/saveCustomFields'; import { RateLimiter } from '../lib'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { saveCustomFields: (fields: IUser['customFields']) => Promise; diff --git a/apps/meteor/app/lib/server/methods/saveSetting.ts b/apps/meteor/app/lib/server/methods/saveSetting.ts index 7f900d1751d82..4f4d29b9fa24a 100644 --- a/apps/meteor/app/lib/server/methods/saveSetting.ts +++ b/apps/meteor/app/lib/server/methods/saveSetting.ts @@ -1,6 +1,6 @@ import type { SettingValue } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { getSettingPermissionId } from '../../../authorization/lib'; import { hasPermissionAsync, hasAllPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { notifyOnSettingChanged } from '../lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { saveSetting(_id: string, value: SettingValue, editor?: string): Promise; diff --git a/apps/meteor/app/lib/server/methods/saveSettings.ts b/apps/meteor/app/lib/server/methods/saveSettings.ts index 8c4f92cfb88f0..dffa986b7217e 100644 --- a/apps/meteor/app/lib/server/methods/saveSettings.ts +++ b/apps/meteor/app/lib/server/methods/saveSettings.ts @@ -1,7 +1,7 @@ import type { ISetting } from '@rocket.chat/core-typings'; import { isSettingCode } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -11,7 +11,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { settings } from '../../../settings/server'; import { notifyOnSettingChangedById } from '../lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { saveSettings( diff --git a/apps/meteor/app/lib/server/methods/sendMessage.ts b/apps/meteor/app/lib/server/methods/sendMessage.ts index a490b5c4c67f6..56009f15fedee 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.ts +++ b/apps/meteor/app/lib/server/methods/sendMessage.ts @@ -1,7 +1,7 @@ import { api } from '@rocket.chat/core-services'; import type { AtLeast, IMessage, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import moment from 'moment'; @@ -12,6 +12,7 @@ import { canSendMessageAsync } from '../../../authorization/server/functions/can import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; +import { MessageTypes } from '../../../ui-utils/server'; import { sendMessage } from '../functions/sendMessage'; import { RateLimiter } from '../lib'; @@ -78,6 +79,8 @@ export async function executeSendMessage(uid: IUser['_id'], message: AtLeast, previewUrls?: string[]): any; @@ -126,6 +129,10 @@ Meteor.methods({ }); } + if (MessageTypes.isSystemMessage(message)) { + throw new Error("Cannot send system messages using 'sendMessage'"); + } + try { return await executeSendMessage(uid, message, previewUrls); } catch (error: any) { diff --git a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts index 2ede129dd380a..5a04032ad82ec 100644 --- a/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts +++ b/apps/meteor/app/lib/server/methods/sendSMTPTestEmail.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import { Meteor } from 'meteor/meteor'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { sendSMTPTestEmail(): { diff --git a/apps/meteor/app/lib/server/methods/setAdminStatus.ts b/apps/meteor/app/lib/server/methods/setAdminStatus.ts index 42ffdc91f85e3..300aa735b014a 100644 --- a/apps/meteor/app/lib/server/methods/setAdminStatus.ts +++ b/apps/meteor/app/lib/server/methods/setAdminStatus.ts @@ -1,12 +1,12 @@ import { isUserFederated } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setAdminStatus(userId: string, admin?: boolean): void; diff --git a/apps/meteor/app/lib/server/methods/setEmail.ts b/apps/meteor/app/lib/server/methods/setEmail.ts index a172eba725552..26540db26b4c8 100644 --- a/apps/meteor/app/lib/server/methods/setEmail.ts +++ b/apps/meteor/app/lib/server/methods/setEmail.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +6,7 @@ import { settings } from '../../../settings/server'; import { setEmail } from '../functions/setEmail'; import { RateLimiter } from '../lib'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setEmail(email: string): string; diff --git a/apps/meteor/app/lib/server/methods/setRealName.ts b/apps/meteor/app/lib/server/methods/setRealName.ts index 2a55bd1021729..f347eef1580ef 100644 --- a/apps/meteor/app/lib/server/methods/setRealName.ts +++ b/apps/meteor/app/lib/server/methods/setRealName.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +6,7 @@ import { settings } from '../../../settings/server'; import { setRealName } from '../functions/setRealName'; import { RateLimiter } from '../lib'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setRealName(name: string): string; diff --git a/apps/meteor/app/lib/server/methods/setUsername.ts b/apps/meteor/app/lib/server/methods/setUsername.ts index 900848f377831..58fac75ed3bd7 100644 --- a/apps/meteor/app/lib/server/methods/setUsername.ts +++ b/apps/meteor/app/lib/server/methods/setUsername.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { setUsernameWithValidation } from '../functions/setUsername'; import { RateLimiter } from '../lib'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setUsername(username: string, param?: { joinDefaultChannelsSilenced?: boolean }): string; diff --git a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts index 0f9349365f4f2..74b74c11e54ae 100644 --- a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts +++ b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts @@ -1,13 +1,13 @@ import { isRegisterUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { unarchiveRoom } from '../functions/unarchiveRoom'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { unarchiveRoom(rid: string): Promise; diff --git a/apps/meteor/app/lib/server/methods/unblockUser.ts b/apps/meteor/app/lib/server/methods/unblockUser.ts index 6c8a9d486bab6..2eec5a082109c 100644 --- a/apps/meteor/app/lib/server/methods/unblockUser.ts +++ b/apps/meteor/app/lib/server/methods/unblockUser.ts @@ -1,9 +1,9 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { unblockUser({ rid, blocked }: { rid: string; blocked: string }): boolean; diff --git a/apps/meteor/app/lib/server/methods/updateMessage.ts b/apps/meteor/app/lib/server/methods/updateMessage.ts index 161a168a2cdcc..8cebe563cd23e 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.ts +++ b/apps/meteor/app/lib/server/methods/updateMessage.ts @@ -1,6 +1,6 @@ import type { IEditedMessage, IMessage, IUser, AtLeast } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import moment from 'moment'; @@ -12,7 +12,11 @@ import { updateMessage } from '../functions/updateMessage'; const allowedEditedFields = ['tshow', 'alias', 'attachments', 'avatar', 'emoji', 'msg', 'customFields', 'content']; -export async function executeUpdateMessage(uid: IUser['_id'], message: AtLeast, previewUrls?: string[]) { +export async function executeUpdateMessage( + uid: IUser['_id'], + message: AtLeast, + previewUrls?: string[], +) { const originalMessage = await Messages.findOneById(message._id); if (!originalMessage?._id) { return; @@ -26,8 +30,11 @@ export async function executeUpdateMessage(uid: IUser['_id'], message: AtLeast = { Meteor.startup(() => { Tracker.autorun(async () => { const bhType = settings.get('Livechat_business_hour_type'); - if (await hasLicense('livechat-enterprise')) { + if (bhType && (await hasLicense('livechat-enterprise'))) { businessHourManager.registerBusinessHourBehavior(businessHours[bhType.toLowerCase()]); } }); diff --git a/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts b/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts index eb0742813c1ff..54c6ba4d75f16 100644 --- a/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts +++ b/apps/meteor/app/livechat/client/externalFrame/generateNewKey.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { sdk } from '../../../utils/client/lib/SDKClient'; diff --git a/apps/meteor/app/livechat/imports/server/rest/rooms.ts b/apps/meteor/app/livechat/imports/server/rest/rooms.ts index f7d5ddb314c92..f80ed61a131e8 100644 --- a/apps/meteor/app/livechat/imports/server/rest/rooms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/rooms.ts @@ -30,7 +30,7 @@ API.v1.addRoute( async get() { const { offset, count } = await getPaginationItems(this.queryParams); const { sort, fields } = await this.parseJsonQuery(); - const { agents, departmentId, open, tags, roomName, onhold } = this.queryParams; + const { agents, departmentId, open, tags, roomName, onhold, queued } = this.queryParams; const { createdAt, customFields, closedAt } = this.queryParams; const createdAtParam = validateDateParams('createdAt', createdAt); @@ -69,6 +69,7 @@ API.v1.addRoute( tags, customFields: parsedCf, onhold, + queued, options: { offset, count, sort, fields }, }), ); diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.ts b/apps/meteor/app/livechat/imports/server/rest/sms.ts index b6669b5f0d89d..6f8ce64bc6352 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/sms.ts @@ -17,6 +17,7 @@ import { Meteor } from 'meteor/meteor'; import { getFileExtension } from '../../../../../lib/utils/getFileExtension'; import { API } from '../../../../api/server'; import { FileUpload } from '../../../../file-upload/server'; +import { checkUrlForSsrf } from '../../../../lib/server/functions/checkUrlForSsrf'; import { settings } from '../../../../settings/server'; import type { ILivechatMessage } from '../../../server/lib/LivechatTyped'; import { Livechat as LivechatTyped } from '../../../server/lib/LivechatTyped'; @@ -24,7 +25,12 @@ import { Livechat as LivechatTyped } from '../../../server/lib/LivechatTyped'; const logger = new Logger('SMS'); const getUploadFile = async (details: Omit, fileUrl: string) => { - const response = await fetch(fileUrl); + const isSsrfSafe = await checkUrlForSsrf(fileUrl); + if (!isSsrfSafe) { + throw new Meteor.Error('error-invalid-url', 'Invalid URL'); + } + + const response = await fetch(fileUrl, { redirect: 'error' }); const content = Buffer.from(await response.arrayBuffer()); diff --git a/apps/meteor/app/livechat/server/api/lib/rooms.ts b/apps/meteor/app/livechat/server/api/lib/rooms.ts index b130e5c2c73a4..26449dce39631 100644 --- a/apps/meteor/app/livechat/server/api/lib/rooms.ts +++ b/apps/meteor/app/livechat/server/api/lib/rooms.ts @@ -14,6 +14,7 @@ export async function findRooms({ tags, customFields, onhold, + queued, options: { offset, count, fields, sort }, }: { agents?: Array; @@ -31,6 +32,7 @@ export async function findRooms({ tags?: Array; customFields?: Record; onhold?: string | boolean; + queued?: string | boolean; options: { offset: number; count: number; fields: Record; sort: Record }; }): Promise }>> { const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}); @@ -44,6 +46,7 @@ export async function findRooms({ tags, customFields, onhold: ['t', 'true', '1'].includes(`${onhold}`), + queued: ['t', 'true', '1'].includes(`${queued}`), options: { sort: sort || { ts: -1 }, offset, diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index 8a663fb0bd6d6..b0f45a63ff87d 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -1,7 +1,7 @@ import { Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatAgent, IUser, SelectedAgent, TransferByData } from '@rocket.chat/core-typings'; import { isOmnichannelRoom, OmnichannelSourceType } from '@rocket.chat/core-typings'; -import { LivechatVisitors, Users, LivechatRooms, Subscriptions, Messages } from '@rocket.chat/models'; +import { LivechatVisitors, Users, LivechatRooms, Messages } from '@rocket.chat/models'; import { isLiveChatRoomForwardProps, isPOSTLivechatRoomCloseParams, @@ -21,6 +21,7 @@ import { isWidget } from '../../../../api/server/helpers/isWidget'; import { canAccessRoomAsync, roomAccessAttributes } from '../../../../authorization/server'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { addUserToRoom } from '../../../../lib/server/functions/addUserToRoom'; +import { closeLivechatRoom } from '../../../../lib/server/functions/closeLivechatRoom'; import { settings as rcSettings } from '../../../../settings/server'; import { normalizeTransferredByData } from '../../lib/Helper'; import type { CloseRoomParams } from '../../lib/LivechatTyped'; @@ -178,51 +179,7 @@ API.v1.addRoute( async post() { const { rid, comment, tags, generateTranscriptPdf, transcriptEmail } = this.bodyParams; - const room = await LivechatRooms.findOneById(rid); - if (!room || !isOmnichannelRoom(room)) { - throw new Error('error-invalid-room'); - } - - if (!room.open) { - throw new Error('error-room-already-closed'); - } - - const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, this.userId, { projection: { _id: 1 } }); - if (!subscription && !(await hasPermissionAsync(this.userId, 'close-others-livechat-room'))) { - throw new Error('error-not-authorized'); - } - - const options: CloseRoomParams['options'] = { - clientAction: true, - tags, - ...(generateTranscriptPdf && { pdfTranscript: { requestedBy: this.userId } }), - ...(transcriptEmail && { - ...(transcriptEmail.sendToVisitor - ? { - emailTranscript: { - sendToVisitor: true, - requestData: { - email: transcriptEmail.requestData.email, - subject: transcriptEmail.requestData.subject, - requestedAt: new Date(), - requestedBy: this.user, - }, - }, - } - : { - emailTranscript: { - sendToVisitor: false, - }, - }), - }), - }; - - await LivechatTyped.closeRoom({ - room, - user: this.user, - options, - comment, - }); + await closeLivechatRoom(this.user, rid, { comment, tags, generateTranscriptPdf, transcriptEmail }); return API.v1.success(); }, @@ -336,8 +293,7 @@ API.v1.addRoute( throw new Error('error-invalid-visitor'); } - const transferedBy = this.user satisfies TransferByData; - transferData.transferredBy = normalizeTransferredByData(transferedBy, room); + transferData.transferredBy = normalizeTransferredByData(this.user, room); if (transferData.userId) { const userToTransfer = await Users.findOneById(transferData.userId); if (userToTransfer) { diff --git a/apps/meteor/app/livechat/server/externalFrame/generateNewKey.ts b/apps/meteor/app/livechat/server/externalFrame/generateNewKey.ts index 11463bcf00d48..a8f145f47d394 100644 --- a/apps/meteor/app/livechat/server/externalFrame/generateNewKey.ts +++ b/apps/meteor/app/livechat/server/externalFrame/generateNewKey.ts @@ -1,7 +1,7 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { omnichannelExternalFrameGenerateKey(): unknown; diff --git a/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts b/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts new file mode 100644 index 0000000000000..372704d339bb7 --- /dev/null +++ b/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts @@ -0,0 +1,24 @@ +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatRooms } from '@rocket.chat/models'; + +import { callbacks } from '../../../../lib/callbacks'; + +callbacks.add( + 'afterSaveMessage', + async (message, room) => { + if (!isOmnichannelRoom(room)) { + return message; + } + + const updater = LivechatRooms.getUpdater(); + const result = await callbacks.run('afterOmnichannelSaveMessage', message, { room, roomUpdater: updater }); + + if (updater.hasChanges()) { + await updater.persist({ _id: room._id }); + } + + return result; + }, + callbacks.priority.MEDIUM, + 'after-omnichannel-save-message', +); diff --git a/apps/meteor/app/livechat/server/hooks/leadCapture.ts b/apps/meteor/app/livechat/server/hooks/leadCapture.ts index 4b987c00c02e5..6a3826b8ba116 100644 --- a/apps/meteor/app/livechat/server/hooks/leadCapture.ts +++ b/apps/meteor/app/livechat/server/hooks/leadCapture.ts @@ -1,5 +1,5 @@ import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; @@ -31,12 +31,8 @@ function validateMessage(message: IMessage, room: IOmnichannelRoom) { } callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { if (!validateMessage(message, room)) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts index f0bfb8574e6af..23131cee60a28 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts @@ -1,15 +1,11 @@ -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (!message || isEditedMessage(message)) { return message; @@ -21,7 +17,7 @@ callbacks.add( } // check if room is yet awaiting for response - if (typeof room.t !== 'undefined' && room.t === 'l' && room.waitingResponse) { + if (room.waitingResponse) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts index 48ec985aa42cf..d9c539182b063 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts @@ -1,78 +1,74 @@ -import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import type { IOmnichannelRoom, IMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage, isMessageFromVisitor } from '@rocket.chat/core-typings'; +import type { Updater } from '@rocket.chat/models'; import { LivechatRooms, LivechatVisitors, LivechatInquiry } from '@rocket.chat/models'; import moment from 'moment'; import { callbacks } from '../../../../lib/callbacks'; import { notifyOnLivechatInquiryChanged } from '../../../lib/server/lib/notifyListener'; -callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - - // skips this callback if the message was edited - if (!message || isEditedMessage(message)) { - return message; - } +export async function markRoomResponded( + message: IMessage, + room: IOmnichannelRoom, + roomUpdater: Updater, +): Promise { + if (message.t || isEditedMessage(message) || isMessageFromVisitor(message)) { + return; + } - // skips this callback if the message is a system message - if (message.t) { - return message; - } + const monthYear = moment().format('YYYY-MM'); + const isVisitorActive = await LivechatVisitors.isVisitorActiveOnPeriod(room.v._id, monthYear); - // if the message has a token, it was sent by the visitor, so ignore it - if (message.token) { - return message; - } + // Case: agent answers & visitor is not active, we mark visitor as active + if (!isVisitorActive) { + await LivechatVisitors.markVisitorActiveForPeriod(room.v._id, monthYear); + } - // Return YYYY-MM from moment - const monthYear = moment().format('YYYY-MM'); - const isVisitorActive = await LivechatVisitors.isVisitorActiveOnPeriod(room.v._id, monthYear); + if (!room.v?.activity?.includes(monthYear)) { + const [, livechatInquiry] = await Promise.all([ + LivechatRooms.markVisitorActiveForPeriod(room._id, monthYear), + LivechatInquiry.markInquiryActiveForPeriod(room._id, monthYear), + ]); - // Case: agent answers & visitor is not active, we mark visitor as active - if (!isVisitorActive) { - await LivechatVisitors.markVisitorActiveForPeriod(room.v._id, monthYear); + if (livechatInquiry) { + void notifyOnLivechatInquiryChanged(livechatInquiry, 'updated', { v: livechatInquiry.v }); } + } - if (!room.v?.activity?.includes(monthYear)) { - const [, livechatInquiry] = await Promise.all([ - LivechatRooms.markVisitorActiveForPeriod(room._id, monthYear), - LivechatInquiry.markInquiryActiveForPeriod(room._id, monthYear), - ]); - if (livechatInquiry) { - void notifyOnLivechatInquiryChanged(livechatInquiry, 'updated', { v: livechatInquiry.v }); - } - } + if (room.responseBy) { + LivechatRooms.getAgentLastMessageTsUpdateQuery(roomUpdater); + } + if (!room.waitingResponse) { + // case where agent sends second message or any subsequent message in a room before visitor responds to the first message + // in this case, we just need to update the lastMessageTs of the responseBy object if (room.responseBy) { - await LivechatRooms.setAgentLastMessageTs(room._id); + LivechatRooms.getAgentLastMessageTsUpdateQuery(roomUpdater); } - // check if room is yet awaiting for response from visitor - if (!room.waitingResponse) { - // case where agent sends second message or any subsequent message in a room before visitor responds to the first message - // in this case, we just need to update the lastMessageTs of the responseBy object - if (room.responseBy) { - await LivechatRooms.setAgentLastMessageTs(room._id); - } - return message; - } + return room.responseBy; + } + + const responseBy: IOmnichannelRoom['responseBy'] = room.responseBy || { + _id: message.u._id, + username: message.u.username, + firstResponseTs: new Date(message.ts), + lastMessageTs: new Date(message.ts), + }; - // This is the first message from agent after visitor had last responded - const responseBy: IOmnichannelRoom['responseBy'] = room.responseBy || { - _id: message.u._id, - username: message.u.username, - firstResponseTs: new Date(message.ts), - lastMessageTs: new Date(message.ts), - }; + LivechatRooms.getResponseByRoomIdUpdateQuery(responseBy, roomUpdater); - // this unsets waitingResponse and sets responseBy object - await LivechatRooms.setResponseByRoomId(room._id, responseBy); + return responseBy; +} + +callbacks.add( + 'afterOmnichannelSaveMessage', + async (message, { room, roomUpdater }) => { + if (!message || message.t || isEditedMessage(message) || isMessageFromVisitor(message)) { + return; + } - return message; + await markRoomResponded(message, room, roomUpdater); }, callbacks.priority.HIGH, 'markRoomResponded', diff --git a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts index e92e6b4d940b3..fef6ad0936f8b 100644 --- a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts +++ b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts @@ -1,84 +1,78 @@ -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; +import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; -callbacks.add( - 'afterSaveMessage', - async (message, room) => { - // check if room is livechat - if (!isOmnichannelRoom(room)) { - return message; - } - - // skips this callback if the message was edited - if (!message || isEditedMessage(message)) { - return message; - } - - // if the message has a token, it was sent by the visitor - if (message.token) { - // When visitor sends a mesage, most metrics wont be calculated/served. - // But, v.lq (last query) will be updated to the message time. This has to be done - // As not doing it will cause the metrics to be crazy and not have real values. - await LivechatRooms.saveAnalyticsDataByRoomId(room, message); - return message; - } +const getMetricValue = (metric: T | undefined, defaultValue: T): T => metric ?? defaultValue; +const calculateTimeDifference = (startTime: T, now: Date): number => + (now.getTime() - new Date(startTime).getTime()) / 1000; +const calculateAvgResponseTime = (totalResponseTime: number, newResponseTime: number, responseCount: number) => + (totalResponseTime + newResponseTime) / (responseCount + 1); - if (message.file) { - message = { ...(await normalizeMessageFileUpload(message)), ...{ _updatedAt: message._updatedAt } }; - } +const getFirstResponseAnalytics = ( + visitorLastQuery: Date, + agentJoinTime: Date, + totalResponseTime: number, + responseCount: number, + now: Date, +) => { + const responseTime = calculateTimeDifference(visitorLastQuery, now); + const reactionTime = calculateTimeDifference(agentJoinTime, now); + const avgResponseTime = calculateAvgResponseTime(totalResponseTime, responseTime, responseCount); - const now = new Date(); - let analyticsData; + return { + firstResponseDate: now, + firstResponseTime: responseTime, + responseTime, + avgResponseTime, + firstReactionDate: now, + firstReactionTime: reactionTime, + reactionTime, + }; +}; - const visitorLastQuery = room.metrics?.v ? room.metrics.v.lq : room.ts; - const agentLastReply = room.metrics?.servedBy ? room.metrics.servedBy.lr : room.ts; - const agentJoinTime = room.servedBy?.ts ? room.servedBy.ts : room.ts; +const getSubsequentResponseAnalytics = (visitorLastQuery: Date, totalResponseTime: number, responseCount: number, now: Date) => { + const responseTime = calculateTimeDifference(visitorLastQuery, now); + const avgResponseTime = calculateAvgResponseTime(totalResponseTime, responseTime, responseCount); - const isResponseTt = room.metrics?.response?.tt; - const isResponseTotal = room.metrics?.response?.total; + return { + responseTime, + avgResponseTime, + reactionTime: responseTime, + }; +}; - if (agentLastReply === room.ts) { - // first response - const firstResponseDate = now; - const firstResponseTime = (now.getTime() - new Date(visitorLastQuery).getTime()) / 1000; - const responseTime = (now.getTime() - new Date(visitorLastQuery).getTime()) / 1000; - const avgResponseTime = - ((isResponseTt ? room.metrics?.response?.tt : 0) || 0 + responseTime) / - ((isResponseTotal ? room.metrics?.response?.total : 0) || 0 + 1); +const getAnalyticsData = (room: IOmnichannelRoom, now: Date): Record | undefined => { + const visitorLastQuery = getMetricValue(room.metrics?.v?.lq, room.ts); + const agentLastReply = getMetricValue(room.metrics?.servedBy?.lr, room.ts); + const agentJoinTime = getMetricValue(room.servedBy?.ts, room.ts); + const totalResponseTime = getMetricValue(room.metrics?.response?.tt, 0); + const responseCount = getMetricValue(room.metrics?.response?.total, 0); - const firstReactionDate = now; - const firstReactionTime = (now.getTime() - new Date(agentJoinTime).getTime()) / 1000; - const reactionTime = (now.getTime() - new Date(agentJoinTime).getTime()) / 1000; + if (agentLastReply === room.ts) { + return getFirstResponseAnalytics(visitorLastQuery, agentJoinTime, totalResponseTime, responseCount, now); + } + if (visitorLastQuery > agentLastReply) { + return getSubsequentResponseAnalytics(visitorLastQuery, totalResponseTime, responseCount, now); + } +}; - analyticsData = { - firstResponseDate, - firstResponseTime, - responseTime, - avgResponseTime, - firstReactionDate, - firstReactionTime, - reactionTime, - }; - } else if (visitorLastQuery > agentLastReply) { - // response, not first - const responseTime = (now.getTime() - new Date(visitorLastQuery).getTime()) / 1000; - const avgResponseTime = - ((isResponseTt ? room.metrics?.response?.tt : 0) || 0 + responseTime) / - ((isResponseTotal ? room.metrics?.response?.total : 0) || 0 + 1); +callbacks.add( + 'afterOmnichannelSaveMessage', + async (message, { room, roomUpdater }) => { + if (!message || isEditedMessage(message)) { + return message; + } - const reactionTime = (now.getTime() - new Date(visitorLastQuery).getTime()) / 1000; + if (message.file) { + message = { ...(await normalizeMessageFileUpload(message)), ...{ _updatedAt: message._updatedAt } }; + } - analyticsData = { - responseTime, - avgResponseTime, - reactionTime, - }; - } // ignore, its continuing response + const analyticsData = getAnalyticsData(room, new Date()); + await LivechatRooms.getAnalyticsUpdateQueryByRoomId(room, message, analyticsData, roomUpdater); - await LivechatRooms.saveAnalyticsDataByRoomId(room, message, analyticsData); return message; }, callbacks.priority.LOW, diff --git a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts index e65f1d99b8842..1925e135a562c 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts @@ -1,4 +1,4 @@ -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatInquiry } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; @@ -7,9 +7,9 @@ import { settings } from '../../../settings/server'; import { RoutingManager } from '../lib/RoutingManager'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room) || isEditedMessage(message) || message.t) { + 'afterOmnichannelSaveMessage', + async (message, { room }) => { + if (isEditedMessage(message) || message.t) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts index 4bc28c3990ba1..03dcfdbf81bd2 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts @@ -1,22 +1,18 @@ -import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isMessageFromVisitor } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!(isOmnichannelRoom(room) && room.v.token)) { - return message; - } - if (message.t) { - return message; - } - if (!message.token) { + 'afterOmnichannelSaveMessage', + async (message, { roomUpdater }) => { + if (message.t || !isMessageFromVisitor(message)) { return message; } - await LivechatRooms.setVisitorLastMessageTimestampByRoomId(room._id, message.ts); + await LivechatRooms.getVisitorLastMessageTsUpdateQueryByRoomId(message.ts, roomUpdater); + + return message; }, callbacks.priority.HIGH, 'save-last-visitor-message-timestamp', diff --git a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts index 24e1d685a0e6b..b3624bd3ecf63 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts +++ b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts @@ -261,13 +261,8 @@ callbacks.add( ); callbacks.add( - 'afterSaveMessage', - async (message, room) => { - // only call webhook if it is a livechat room - if (!isOmnichannelRoom(room) || !room?.v?.token) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // if the message has a token, it was sent from the visitor // if not, it was sent from the agent if (message.token && !settings.get('Livechat_webhook_on_visitor_message')) { diff --git a/apps/meteor/app/livechat/server/index.ts b/apps/meteor/app/livechat/server/index.ts index fc96f2a921a96..9a1f40238df54 100644 --- a/apps/meteor/app/livechat/server/index.ts +++ b/apps/meteor/app/livechat/server/index.ts @@ -16,6 +16,7 @@ import './hooks/saveContactLastChat'; import './hooks/saveLastMessageToInquiry'; import './hooks/afterUserActions'; import './hooks/afterAgentRemoved'; +import './hooks/afterSaveOmnichannelMessage'; import './methods/addAgent'; import './methods/addManager'; import './methods/changeLivechatStatus'; diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 01bb2ff34e9aa..c0e85a8c7c2b2 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -142,7 +142,9 @@ export const createLivechatRoom = async < await callbacks.run('livechat.newRoom', room); - await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room); + // TODO: replace with `Message.saveSystemMessage` + + await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false, token: guest.token }, room); return result.value as IOmnichannelRoom; }; @@ -646,6 +648,7 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi '', { _id, username }, { + ...(transferData.transferredBy.userType === 'visitor' && { token: room.v.token }), transferData: { ...transferData, prevDepartment: transferData.originalDepartmentName, @@ -681,18 +684,23 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi return true; }; -export const normalizeTransferredByData = (transferredBy: TransferByData, room: IOmnichannelRoom) => { +type MakePropertyOptional = Omit & { [P in K]?: T[P] }; + +export const normalizeTransferredByData = ( + transferredBy: MakePropertyOptional, + room: IOmnichannelRoom, +): TransferByData => { if (!transferredBy || !room) { throw new Error('You must provide "transferredBy" and "room" params to "getTransferredByData"'); } const { servedBy: { _id: agentId } = {} } = room; const { _id, username, name, userType: transferType } = transferredBy; - const type = transferType || (_id === agentId ? 'agent' : 'user'); + const userType = transferType || (_id === agentId ? 'agent' : 'user'); return { _id, username, ...(name && { name }), - type, + userType, }; }; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index bb56eb81ceb31..ccca7a8eb68e6 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -9,7 +9,6 @@ import type { IUser, MessageTypesValues, ILivechatVisitor, - IOmnichannelSystemMessage, SelectedAgent, ILivechatAgent, IMessage, @@ -41,7 +40,6 @@ import { import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import moment from 'moment-timezone'; import type { Filter, FindCursor } from 'mongodb'; import UAParser from 'ua-parser-js'; @@ -68,12 +66,14 @@ import { import * as Mailer from '../../../mailer/server/api'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; -import { getTimezone } from '../../../utils/server/lib/getTimezone'; import { businessHourManager } from '../business-hour'; import { parseAgentCustomFields, updateDepartmentAgents, validateEmail, normalizeTransferredByData } from './Helper'; import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable'; +import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor } from './localTypes'; +import { parseTranscriptRequest } from './parseTranscriptRequest'; +import { sendTranscript as sendTranscriptFunc } from './sendTranscript'; type RegisterGuestType = Partial> & { id?: string; @@ -82,36 +82,6 @@ type RegisterGuestType = Partial; - }; - pdfTranscript?: { - requestedBy: string; - }; - }; -}; - -export type CloseRoomParamsByUser = { - user: IUser | null; -} & GenericCloseRoomParams; - -export type CloseRoomParamsByVisitor = { - visitor: ILivechatVisitor; -} & GenericCloseRoomParams; - -export type CloseRoomParams = CloseRoomParamsByUser | CloseRoomParamsByVisitor; - type OfflineMessageData = { message: string; name: string; @@ -326,12 +296,8 @@ class LivechatClass { this.logger.debug(`DB updated for room ${room._id}`); - const message = { - t: 'livechat-close', - msg: comment, - groupable: false, - transcriptRequested: !!transcriptRequest, - }; + const transcriptRequested = + !!transcriptRequest || (!settings.get('Livechat_enable_transcript') && settings.get('Livechat_transcript_send_always')); // Retrieve the closed room const newRoom = await LivechatRooms.findOneById(rid); @@ -341,9 +307,21 @@ class LivechatClass { } this.logger.debug(`Sending closing message to room ${room._id}`); - await sendMessage(chatCloser, message, newRoom); + await sendMessage( + chatCloser, + { + t: 'livechat-close', + msg: comment, + groupable: false, + transcriptRequested, + ...(isRoomClosedByVisitorParams(params) && { token: chatCloser.token }), + }, + newRoom, + ); - await Message.saveSystemMessage('command', rid, 'promptTranscript', closeData.closedBy); + if (settings.get('Livechat_enable_transcript') && !settings.get('Livechat_transcript_send_always')) { + await Message.saveSystemMessage('command', rid, 'promptTranscript', closeData.closedBy); + } this.logger.debug(`Running callbacks for room ${newRoom._id}`); @@ -355,15 +333,18 @@ class LivechatClass { void Apps.self?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, newRoom); void Apps.self?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, newRoom); }); + + const visitor = isRoomClosedByVisitorParams(params) ? params.visitor : undefined; + const opts = await parseTranscriptRequest(params.room, options, visitor); if (process.env.TEST_MODE) { await callbacks.run('livechat.closeRoom', { room: newRoom, - options, + options: opts, }); } else { callbacks.runAsync('livechat.closeRoom', { room: newRoom, - options, + options: opts, }); } @@ -562,127 +543,6 @@ class LivechatClass { } } - async sendTranscript({ - token, - rid, - email, - subject, - user, - }: { - token: string; - rid: string; - email: string; - subject?: string; - user?: Pick | null; - }): Promise { - check(rid, String); - check(email, String); - this.logger.debug(`Sending conversation transcript of room ${rid} to user with token ${token}`); - - const room = await LivechatRooms.findOneById(rid); - - const visitor = await LivechatVisitors.getVisitorByToken(token, { - projection: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, - }); - - if (!visitor) { - throw new Error('error-invalid-token'); - } - - // @ts-expect-error - Visitor typings should include language? - const userLanguage = visitor?.language || settings.get('Language') || 'en'; - const timezone = getTimezone(user); - this.logger.debug(`Transcript will be sent using ${timezone} as timezone`); - - if (!room) { - throw new Error('error-invalid-room'); - } - - // allow to only user to send transcripts from their own chats - if (room.t !== 'l' || !room.v || room.v.token !== token) { - throw new Error('error-invalid-room'); - } - - const showAgentInfo = settings.get('Livechat_show_agent_info'); - const closingMessage = await Messages.findLivechatClosingMessage(rid, { projection: { ts: 1 } }); - const ignoredMessageTypes: MessageTypesValues[] = [ - 'livechat_navigation_history', - 'livechat_transcript_history', - 'command', - 'livechat-close', - 'livechat-started', - 'livechat_video_call', - ]; - const messages = await Messages.findVisibleByRoomIdNotContainingTypesBeforeTs( - rid, - ignoredMessageTypes, - closingMessage?.ts ? new Date(closingMessage.ts) : new Date(), - { - sort: { ts: 1 }, - }, - ); - - let html = '

'; - await messages.forEach((message) => { - let author; - if (message.u._id === visitor._id) { - author = i18n.t('You', { lng: userLanguage }); - } else { - author = showAgentInfo ? message.u.name || message.u.username : i18n.t('Agent', { lng: userLanguage }); - } - - const datetime = moment.tz(message.ts, timezone).locale(userLanguage).format('LLL'); - const singleMessage = ` -

${author} ${datetime}

-

${message.msg}

- `; - html += singleMessage; - }); - - html = `${html}
`; - - const fromEmail = settings.get('From_Email').match(/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i); - let emailFromRegexp = ''; - if (fromEmail) { - emailFromRegexp = fromEmail[0]; - } else { - emailFromRegexp = settings.get('From_Email'); - } - - const mailSubject = subject || i18n.t('Transcript_of_your_livechat_conversation', { lng: userLanguage }); - - await this.sendEmail(emailFromRegexp, email, emailFromRegexp, mailSubject, html); - - setImmediate(() => { - void callbacks.run('livechat.sendTranscript', messages, email); - }); - - const requestData: IOmnichannelSystemMessage['requestData'] = { - type: 'user', - visitor, - user, - }; - - if (!user?.username) { - const cat = await Users.findOneById('rocket.cat', { projection: { _id: 1, username: 1, name: 1 } }); - if (cat) { - requestData.user = cat; - requestData.type = 'visitor'; - } - } - - if (!requestData.user) { - this.logger.error('rocket.cat user not found'); - throw new Error('No user provided and rocket.cat not found'); - } - - await Message.saveSystemMessage('livechat_transcript_history', room._id, '', requestData.user, { - requestData, - }); - - return true; - } - async registerGuest({ id, token, @@ -712,7 +572,9 @@ class LivechatClass { visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; } - if (department) { + const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); + + if (livechatVisitor?.department !== department && department) { Livechat.logger.debug(`Attempt to find a department with id/name ${department}`); const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } }); if (!dep) { @@ -723,8 +585,6 @@ class LivechatClass { visitorDataToUpdate.department = dep._id; } - const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); - visitorDataToUpdate.token = livechatVisitor?.token || token; let existingUser = null; @@ -1267,7 +1127,7 @@ class LivechatClass { if (guest.name) { message.alias = guest.name; } - return Object.assign(await sendMessage(guest, message, room), { + return Object.assign(await sendMessage(guest, { ...message, token: guest.token }, room), { newRoom, showConnecting: this.showConnecting(), }); @@ -1386,7 +1246,7 @@ class LivechatClass { _id: String, username: String, name: Match.Maybe(String), - type: String, + userType: String, }), ); @@ -1394,34 +1254,31 @@ class LivechatClass { const scopeData = scope || (nextDepartment ? 'department' : 'agent'); this.logger.info(`Storing new chat transfer of ${room._id} [Transfered by: ${_id} to ${scopeData}]`); - const transfer = { - transferData: { - transferredBy, + await sendMessage( + transferredBy, + { + t: 'livechat_transfer_history', + rid: room._id, ts: new Date(), - scope: scopeData, - comment, - ...(previousDepartment && { previousDepartment }), - ...(nextDepartment && { nextDepartment }), - ...(transferredTo && { transferredTo }), - }, - }; - - const type = 'livechat_transfer_history'; - const transferMessage = { - t: type, - rid: room._id, - ts: new Date(), - msg: '', - u: { - _id, - username, + msg: '', + u: { + _id, + username, + }, + groupable: false, + ...(transferData.transferredBy.userType === 'visitor' && { token: room.v.token }), + transferData: { + transferredBy, + ts: new Date(), + scope: scopeData, + comment, + ...(previousDepartment && { previousDepartment }), + ...(nextDepartment && { nextDepartment }), + ...(transferredTo && { transferredTo }), + }, }, - groupable: false, - }; - - Object.assign(transferMessage, transfer); - - await sendMessage(transferredBy, transferMessage, room); + room, + ); } async saveGuest(guestData: Pick & { email?: string; phone?: string }, userId: string) { @@ -1984,6 +1841,23 @@ class LivechatClass { return departmentDB; } + + async sendTranscript({ + token, + rid, + email, + subject, + user, + }: { + token: string; + rid: string; + email: string; + subject?: string; + user?: Pick | null; + }): Promise { + return sendTranscriptFunc({ token, rid, email, subject, user }); + } } export const Livechat = new LivechatClass(); +export * from './localTypes'; diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index 5ae03e0ee03bf..2075a5e9af975 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -15,6 +15,7 @@ import { Random } from '@rocket.chat/random'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { dispatchInquiryPosition } from '../../../../ee/app/livechat-enterprise/server/lib/Helper'; import { callbacks } from '../../../../lib/callbacks'; import { sendNotification } from '../../../lib/server'; import { @@ -27,6 +28,7 @@ import { i18n } from '../../../utils/lib/i18n'; import { createLivechatRoom, createLivechatInquiry, allowAgentSkipQueue } from './Helper'; import { Livechat } from './LivechatTyped'; import { RoutingManager } from './RoutingManager'; +import { getInquirySortMechanismSetting } from './settings'; const logger = new Logger('QueueManager'); @@ -259,6 +261,18 @@ export class QueueManager { throw new Error('room-not-found'); } + if (!newRoom.servedBy && settings.get('Omnichannel_calculate_dispatch_service_queue_statistics')) { + const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ + inquiryId: inquiry._id, + department, + queueSortBy: getInquirySortMechanismSetting(), + }); + + if (inq) { + void dispatchInquiryPosition(inq); + } + } + return newRoom; } diff --git a/apps/meteor/app/livechat/server/lib/localTypes.ts b/apps/meteor/app/livechat/server/lib/localTypes.ts new file mode 100644 index 0000000000000..c6acbbc5bcbd6 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/localTypes.ts @@ -0,0 +1,31 @@ +import type { IOmnichannelRoom, IUser, ILivechatVisitor } from '@rocket.chat/core-typings'; + +export type GenericCloseRoomParams = { + room: IOmnichannelRoom; + comment?: string; + options?: { + clientAction?: boolean; + tags?: string[]; + emailTranscript?: + | { + sendToVisitor: false; + } + | { + sendToVisitor: true; + requestData: NonNullable; + }; + pdfTranscript?: { + requestedBy: string; + }; + }; +}; + +export type CloseRoomParamsByUser = { + user: IUser | null; +} & GenericCloseRoomParams; + +export type CloseRoomParamsByVisitor = { + visitor: ILivechatVisitor; +} & GenericCloseRoomParams; + +export type CloseRoomParams = CloseRoomParamsByUser | CloseRoomParamsByVisitor; diff --git a/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts new file mode 100644 index 0000000000000..76595a7ff6402 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/parseTranscriptRequest.ts @@ -0,0 +1,61 @@ +import type { ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import { LivechatVisitors, Users } from '@rocket.chat/models'; + +import { settings } from '../../../settings/server'; +import type { CloseRoomParams } from './localTypes'; + +export const parseTranscriptRequest = async ( + room: IOmnichannelRoom, + options: CloseRoomParams['options'], + visitor?: ILivechatVisitor, + user?: IUser, +): Promise => { + const visitorDecideTranscript = settings.get('Livechat_enable_transcript'); + // visitor decides, no changes + if (visitorDecideTranscript) { + return options; + } + + // send always is disabled, no changes + const sendAlways = settings.get('Livechat_transcript_send_always'); + if (!sendAlways) { + return options; + } + + const visitorData = + visitor || + (await LivechatVisitors.findOneById>(room.v._id, { projection: { visitorEmails: 1 } })); + // no visitor, no changes + if (!visitorData) { + return options; + } + const visitorEmail = visitorData?.visitorEmails?.[0]?.address; + // visitor doesnt have email, no changes + if (!visitorEmail) { + return options; + } + + const defOptions = { projection: { _id: 1, username: 1, name: 1 } }; + const requestedBy = + user || + (room.servedBy && (await Users.findOneById(room.servedBy._id, defOptions))) || + (await Users.findOneById('rocket.cat', defOptions)); + + // no user available for backing request, no changes + if (!requestedBy) { + return options; + } + + return { + ...options, + emailTranscript: { + sendToVisitor: true, + requestData: { + email: visitorEmail, + requestedAt: new Date(), + subject: '', + requestedBy, + }, + }, + }; +}; diff --git a/apps/meteor/app/livechat/server/lib/sendTranscript.ts b/apps/meteor/app/livechat/server/lib/sendTranscript.ts new file mode 100644 index 0000000000000..74032121ee509 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/sendTranscript.ts @@ -0,0 +1,227 @@ +import { Message } from '@rocket.chat/core-services'; +import { + type IUser, + type MessageTypesValues, + type IOmnichannelSystemMessage, + isFileAttachment, + isFileImageAttachment, +} from '@rocket.chat/core-typings'; +import colors from '@rocket.chat/fuselage-tokens/colors'; +import { Logger } from '@rocket.chat/logger'; +import { LivechatRooms, LivechatVisitors, Messages, Uploads, Users } from '@rocket.chat/models'; +import { check } from 'meteor/check'; +import moment from 'moment-timezone'; + +import { callbacks } from '../../../../lib/callbacks'; +import { i18n } from '../../../../server/lib/i18n'; +import { FileUpload } from '../../../file-upload/server'; +import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; +import { MessageTypes } from '../../../ui-utils/lib/MessageTypes'; +import { getTimezone } from '../../../utils/server/lib/getTimezone'; + +const logger = new Logger('Livechat-SendTranscript'); + +export async function sendTranscript({ + token, + rid, + email, + subject, + user, +}: { + token: string; + rid: string; + email: string; + subject?: string; + user?: Pick | null; +}): Promise { + check(rid, String); + check(email, String); + logger.debug(`Sending conversation transcript of room ${rid} to user with token ${token}`); + + const room = await LivechatRooms.findOneById(rid); + + const visitor = await LivechatVisitors.getVisitorByToken(token, { + projection: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, + }); + + if (!visitor) { + throw new Error('error-invalid-token'); + } + + // @ts-expect-error - Visitor typings should include language? + const userLanguage = visitor?.language || settings.get('Language') || 'en'; + const timezone = getTimezone(user); + logger.debug(`Transcript will be sent using ${timezone} as timezone`); + + if (!room) { + throw new Error('error-invalid-room'); + } + + // allow to only user to send transcripts from their own chats + if (room.t !== 'l' || !room.v || room.v.token !== token) { + throw new Error('error-invalid-room'); + } + + const showAgentInfo = settings.get('Livechat_show_agent_info'); + const showSystemMessages = settings.get('Livechat_transcript_show_system_messages'); + const closingMessage = await Messages.findLivechatClosingMessage(rid, { projection: { ts: 1 } }); + const ignoredMessageTypes: MessageTypesValues[] = [ + 'livechat_navigation_history', + 'livechat_transcript_history', + 'command', + 'livechat-close', + 'livechat-started', + 'livechat_video_call', + 'omnichannel_priority_change_history', + ]; + const acceptableImageMimeTypes = ['image/jpeg', 'image/png', 'image/jpg']; + const messages = await Messages.findVisibleByRoomIdNotContainingTypesBeforeTs( + rid, + ignoredMessageTypes, + closingMessage?.ts ? new Date(closingMessage.ts) : new Date(), + showSystemMessages, + { + sort: { ts: 1 }, + }, + ); + + let html = '

'; + const InvalidFileMessage = `
${i18n.t( + 'This_attachment_is_not_supported', + { lng: userLanguage }, + )}
`; + + for await (const message of messages) { + let author; + if (message.u._id === visitor._id) { + author = i18n.t('You', { lng: userLanguage }); + } else { + author = showAgentInfo ? message.u.name || message.u.username : i18n.t('Agent', { lng: userLanguage }); + } + + const isSystemMessage = MessageTypes.isSystemMessage(message); + const messageType = isSystemMessage && MessageTypes.getType(message); + + let messageContent = messageType + ? `${i18n.t( + messageType.message, + messageType.data + ? { ...messageType.data(message), interpolation: { escapeValue: false } } + : { interpolation: { escapeValue: false } }, + )}` + : message.msg; + + let filesHTML = ''; + + if (message.attachments && message.attachments?.length > 0) { + messageContent = message.attachments[0].description || ''; + + for await (const attachment of message.attachments) { + if (!isFileAttachment(attachment)) { + // ignore other types of attachments + continue; + } + + if (!isFileImageAttachment(attachment)) { + filesHTML += `
${attachment.title || ''}${InvalidFileMessage}
`; + continue; + } + + if (!attachment.image_type || !acceptableImageMimeTypes.includes(attachment.image_type)) { + filesHTML += `
${attachment.title || ''}${InvalidFileMessage}
`; + continue; + } + + // Image attachment can be rendered in email body + const file = message.files?.find((file) => file.name === attachment.title); + + if (!file) { + filesHTML += `
${attachment.title || ''}${InvalidFileMessage}
`; + continue; + } + + const uploadedFile = await Uploads.findOneById(file._id); + + if (!uploadedFile) { + filesHTML += `
${file.name}${InvalidFileMessage}
`; + continue; + } + + const uploadedFileBuffer = await FileUpload.getBuffer(uploadedFile); + filesHTML += `

${file.name}

`; + } + } + + const datetime = moment.tz(message.ts, timezone).locale(userLanguage).format('LLL'); + const singleMessage = ` +

${author} ${datetime}

+

${messageContent}

+

${filesHTML}

+ `; + html += singleMessage; + } + + html = `${html}
`; + + const fromEmail = settings.get('From_Email').match(/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i); + let emailFromRegexp = ''; + if (fromEmail) { + emailFromRegexp = fromEmail[0]; + } else { + emailFromRegexp = settings.get('From_Email'); + } + + // Some endpoints allow the caller to pass a different `subject` via parameter. + // IF subject is passed, we'll use that one and treat it as an override + // IF no subject is passed, we fallback to the setting `Livechat_transcript_email_subject` + // IF that is not configured, we fallback to 'Transcript of your livechat conversation', which is the default value + // As subject and setting value are user input, we don't translate them + const mailSubject = + subject || + settings.get('Livechat_transcript_email_subject') || + i18n.t('Transcript_of_your_livechat_conversation', { lng: userLanguage }); + + await Mailer.send({ + to: email, + from: emailFromRegexp, + replyTo: emailFromRegexp, + subject: mailSubject, + html, + }); + + setImmediate(() => { + void callbacks.run('livechat.sendTranscript', messages, email); + }); + + const requestData: IOmnichannelSystemMessage['requestData'] = { + type: 'user', + visitor, + user, + }; + + if (!user?.username) { + const cat = await Users.findOneById('rocket.cat', { projection: { _id: 1, username: 1, name: 1 } }); + if (cat) { + requestData.user = cat; + requestData.type = 'visitor'; + } + } + + if (!requestData.user) { + logger.error('rocket.cat user not found'); + throw new Error('No user provided and rocket.cat not found'); + } + + await Message.saveSystemMessage('livechat_transcript_history', room._id, '', requestData.user, { + requestData, + }); + + return true; +} diff --git a/apps/meteor/app/livechat/server/methods/addAgent.ts b/apps/meteor/app/livechat/server/methods/addAgent.ts index 6160db590ece6..0551e985e18e9 100644 --- a/apps/meteor/app/livechat/server/methods/addAgent.ts +++ b/apps/meteor/app/livechat/server/methods/addAgent.ts @@ -1,12 +1,12 @@ import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:addAgent'(username: string): Promise; diff --git a/apps/meteor/app/livechat/server/methods/addManager.ts b/apps/meteor/app/livechat/server/methods/addManager.ts index 347c4f07d63f3..a954d8111773d 100644 --- a/apps/meteor/app/livechat/server/methods/addManager.ts +++ b/apps/meteor/app/livechat/server/methods/addManager.ts @@ -1,12 +1,12 @@ import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:addManager'(username: string): Promise; diff --git a/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts b/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts index 8ccfcd5ec4689..f45588cad6d37 100644 --- a/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts +++ b/apps/meteor/app/livechat/server/methods/changeLivechatStatus.ts @@ -1,13 +1,13 @@ import { ILivechatAgentStatus } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat as LivechatTS } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:changeLivechatStatus'(params?: { status?: ILivechatAgentStatus; agentId?: string }): unknown; diff --git a/apps/meteor/app/livechat/server/methods/closeRoom.ts b/apps/meteor/app/livechat/server/methods/closeRoom.ts index 1374d86ab9f70..19c8b27093893 100644 --- a/apps/meteor/app/livechat/server/methods/closeRoom.ts +++ b/apps/meteor/app/livechat/server/methods/closeRoom.ts @@ -1,6 +1,6 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users, LivechatRooms, Subscriptions as SubscriptionRaw } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; @@ -35,7 +35,7 @@ type LivechatCloseRoomOptions = Omit }; }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:closeRoom'(roomId: string, comment?: string, options?: CloseRoomOptions): void; @@ -60,6 +60,16 @@ Meteor.methods({ }); } + const subscription = await SubscriptionRaw.findOneByRoomIdAndUserId(roomId, userId, { + projection: { + _id: 1, + }, + }); + if (!room.open && subscription) { + await SubscriptionRaw.removeByRoomId(roomId); + return; + } + if (!room.open) { throw new Meteor.Error('room-closed', 'Room closed', { method: 'livechat:closeRoom' }); } @@ -71,11 +81,6 @@ Meteor.methods({ }); } - const subscription = await SubscriptionRaw.findOneByRoomIdAndUserId(roomId, user._id, { - projection: { - _id: 1, - }, - }); if (!subscription && !(await hasPermissionAsync(userId, 'close-others-livechat-room'))) { throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeRoom', diff --git a/apps/meteor/app/livechat/server/methods/discardTranscript.ts b/apps/meteor/app/livechat/server/methods/discardTranscript.ts index d46c8ffea35f4..3265aa9c54d25 100644 --- a/apps/meteor/app/livechat/server/methods/discardTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/discardTranscript.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:discardTranscript'(rid: string): boolean; diff --git a/apps/meteor/app/livechat/server/methods/getAgentData.ts b/apps/meteor/app/livechat/server/methods/getAgentData.ts index d32d24d7f7c1f..5fe58560806e5 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentData.ts +++ b/apps/meteor/app/livechat/server/methods/getAgentData.ts @@ -1,13 +1,13 @@ import type { ILivechatAgent } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, LivechatRooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getAgentData'(params: { diff --git a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts index b5ea8dafe92ca..819bac03c5b41 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts +++ b/apps/meteor/app/livechat/server/methods/getAgentOverviewData.ts @@ -1,13 +1,13 @@ import type { ConversationData } from '@rocket.chat/core-services'; import { OmnichannelAnalytics } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getAgentOverviewData'(options: { chartOptions: { name: string } }): ConversationData | void; diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts index 85c8e033b8f5e..2ff682c1c39c2 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsChartData.ts @@ -1,12 +1,12 @@ import type { ChartDataResult } from '@rocket.chat/core-services'; import { OmnichannelAnalytics } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getAnalyticsChartData'(options: { chartOptions: { name: string } }): ChartDataResult | void; diff --git a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts index c9bb3d163457b..9f56cfa570693 100644 --- a/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts +++ b/apps/meteor/app/livechat/server/methods/getAnalyticsOverviewData.ts @@ -1,14 +1,14 @@ import type { AnalyticsOverviewDataResult } from '@rocket.chat/core-services'; import { OmnichannelAnalytics } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getAnalyticsOverviewData'(options: { analyticsOptions: { name: string } }): AnalyticsOverviewDataResult[] | void; diff --git a/apps/meteor/app/livechat/server/methods/getCustomFields.ts b/apps/meteor/app/livechat/server/methods/getCustomFields.ts index ec0ac35d6c210..36dca08f08596 100644 --- a/apps/meteor/app/livechat/server/methods/getCustomFields.ts +++ b/apps/meteor/app/livechat/server/methods/getCustomFields.ts @@ -1,11 +1,11 @@ import type { ILivechatCustomField } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatCustomField } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getCustomFields'(): ILivechatCustomField[]; diff --git a/apps/meteor/app/livechat/server/methods/getDepartmentForwardRestrictions.ts b/apps/meteor/app/livechat/server/methods/getDepartmentForwardRestrictions.ts index b26d9706a8cd9..2b891514d03e3 100644 --- a/apps/meteor/app/livechat/server/methods/getDepartmentForwardRestrictions.ts +++ b/apps/meteor/app/livechat/server/methods/getDepartmentForwardRestrictions.ts @@ -1,10 +1,10 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getDepartmentForwardRestrictions'(departmentId: string): unknown; diff --git a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts index 197898e8d2b20..d8accf7f84f51 100644 --- a/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts +++ b/apps/meteor/app/livechat/server/methods/getFirstRoomMessage.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms, Messages } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getFirstRoomMessage'(params: { rid: string }): unknown; diff --git a/apps/meteor/app/livechat/server/methods/getNextAgent.ts b/apps/meteor/app/livechat/server/methods/getNextAgent.ts index 53b8441cad2e6..179f1f95dedfa 100644 --- a/apps/meteor/app/livechat/server/methods/getNextAgent.ts +++ b/apps/meteor/app/livechat/server/methods/getNextAgent.ts @@ -1,6 +1,6 @@ import type { ILivechatAgent } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarn import { settings } from '../../../settings/server'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getNextAgent'(params: { diff --git a/apps/meteor/app/livechat/server/methods/getRoutingConfig.ts b/apps/meteor/app/livechat/server/methods/getRoutingConfig.ts index 364853a74511d..02b2840f41cc4 100644 --- a/apps/meteor/app/livechat/server/methods/getRoutingConfig.ts +++ b/apps/meteor/app/livechat/server/methods/getRoutingConfig.ts @@ -1,10 +1,10 @@ import type { OmichannelRoutingConfig } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { RoutingManager } from '../lib/RoutingManager'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getRoutingConfig'(): OmichannelRoutingConfig | undefined; diff --git a/apps/meteor/app/livechat/server/methods/getTagsList.ts b/apps/meteor/app/livechat/server/methods/getTagsList.ts index e9e467d243c8f..b3efe5d026a32 100644 --- a/apps/meteor/app/livechat/server/methods/getTagsList.ts +++ b/apps/meteor/app/livechat/server/methods/getTagsList.ts @@ -1,11 +1,11 @@ import type { ILivechatTag } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:getTagsList'(): ILivechatTag[]; diff --git a/apps/meteor/app/livechat/server/methods/loadHistory.ts b/apps/meteor/app/livechat/server/methods/loadHistory.ts index 373ede1a36105..1004f9dd604dd 100644 --- a/apps/meteor/app/livechat/server/methods/loadHistory.ts +++ b/apps/meteor/app/livechat/server/methods/loadHistory.ts @@ -1,13 +1,13 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { loadMessageHistory } from '../../../lib/server/functions/loadMessageHistory'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:loadHistory'(params: { token: string; rid: string; end?: Date; limit?: number; ls: Date }): diff --git a/apps/meteor/app/livechat/server/methods/loginByToken.ts b/apps/meteor/app/livechat/server/methods/loginByToken.ts index cae23e6d16f7c..3b82413e038af 100644 --- a/apps/meteor/app/livechat/server/methods/loginByToken.ts +++ b/apps/meteor/app/livechat/server/methods/loginByToken.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:loginByToken'(token: string): { _id: string } | undefined; diff --git a/apps/meteor/app/livechat/server/methods/pageVisited.ts b/apps/meteor/app/livechat/server/methods/pageVisited.ts index c226a91de430a..7c0864f27b74a 100644 --- a/apps/meteor/app/livechat/server/methods/pageVisited.ts +++ b/apps/meteor/app/livechat/server/methods/pageVisited.ts @@ -1,10 +1,10 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:pageVisited'(token: string, room: string, pageInfo: { title: string; location: { href: string }; change: string }): void; diff --git a/apps/meteor/app/livechat/server/methods/registerGuest.ts b/apps/meteor/app/livechat/server/methods/registerGuest.ts index 4a531d0c89e52..c6a119d915914 100644 --- a/apps/meteor/app/livechat/server/methods/registerGuest.ts +++ b/apps/meteor/app/livechat/server/methods/registerGuest.ts @@ -1,13 +1,13 @@ import type { ILivechatVisitor, IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, Messages, LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat as LivechatTyped } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:registerGuest'({ diff --git a/apps/meteor/app/livechat/server/methods/removeAgent.ts b/apps/meteor/app/livechat/server/methods/removeAgent.ts index 643ae696ada0d..ebb57383784f0 100644 --- a/apps/meteor/app/livechat/server/methods/removeAgent.ts +++ b/apps/meteor/app/livechat/server/methods/removeAgent.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeAgent'(username: string): boolean; diff --git a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts index 9be699ae05ed7..ba3939bb8573e 100644 --- a/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts +++ b/apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts @@ -1,14 +1,14 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Logger } from '@rocket.chat/logger'; import { LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeAllClosedRooms'(departmentIds?: string[]): Promise; diff --git a/apps/meteor/app/livechat/server/methods/removeCustomField.ts b/apps/meteor/app/livechat/server/methods/removeCustomField.ts index 1d3e7c817738a..ad02df5823377 100644 --- a/apps/meteor/app/livechat/server/methods/removeCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/removeCustomField.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatCustomField } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeCustomField'(_id: string): DeleteResult; diff --git a/apps/meteor/app/livechat/server/methods/removeDepartment.ts b/apps/meteor/app/livechat/server/methods/removeDepartment.ts index 3c46f3a424d6c..6e4b11c836f67 100644 --- a/apps/meteor/app/livechat/server/methods/removeDepartment.ts +++ b/apps/meteor/app/livechat/server/methods/removeDepartment.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; @@ -7,7 +7,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { DepartmentHelper } from '../lib/Departments'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeDepartment'(_id: string): DeleteResult; diff --git a/apps/meteor/app/livechat/server/methods/removeManager.ts b/apps/meteor/app/livechat/server/methods/removeManager.ts index 4c003ff922bca..85a5f3076c8c3 100644 --- a/apps/meteor/app/livechat/server/methods/removeManager.ts +++ b/apps/meteor/app/livechat/server/methods/removeManager.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeManager'(username: string): boolean; diff --git a/apps/meteor/app/livechat/server/methods/removeRoom.ts b/apps/meteor/app/livechat/server/methods/removeRoom.ts index db3fcf2c849fb..751d51d4f019f 100644 --- a/apps/meteor/app/livechat/server/methods/removeRoom.ts +++ b/apps/meteor/app/livechat/server/methods/removeRoom.ts @@ -1,12 +1,12 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeRoom'(rid: IRoom['_id']): void; diff --git a/apps/meteor/app/livechat/server/methods/removeTrigger.ts b/apps/meteor/app/livechat/server/methods/removeTrigger.ts index 69f3a4a2d80c8..41d79d7251768 100644 --- a/apps/meteor/app/livechat/server/methods/removeTrigger.ts +++ b/apps/meteor/app/livechat/server/methods/removeTrigger.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatTrigger } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:removeTrigger'(triggerId: string): boolean; diff --git a/apps/meteor/app/livechat/server/methods/requestTranscript.ts b/apps/meteor/app/livechat/server/methods/requestTranscript.ts index 5abf6a3b72e3b..80cb3a9625fea 100644 --- a/apps/meteor/app/livechat/server/methods/requestTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/requestTranscript.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -7,7 +7,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:requestTranscript'(rid: string, email: string, subject: string): Promise; diff --git a/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts b/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts index 38b58b9d2d426..bf76519a5afb7 100644 --- a/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/returnAsInquiry.ts @@ -1,13 +1,13 @@ import { Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatDepartment, IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:returnAsInquiry'(rid: IRoom['_id'], departmentID?: ILivechatDepartment['_id']): boolean; diff --git a/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts b/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts index 90d9815d4cf10..fe542b67156e7 100644 --- a/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts +++ b/apps/meteor/app/livechat/server/methods/saveAgentInfo.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveAgentInfo'(_id: string, agentData: Record, agentDepartments: string[]): unknown; diff --git a/apps/meteor/app/livechat/server/methods/saveAppearance.ts b/apps/meteor/app/livechat/server/methods/saveAppearance.ts index cac5d34264d20..619dae1477087 100644 --- a/apps/meteor/app/livechat/server/methods/saveAppearance.ts +++ b/apps/meteor/app/livechat/server/methods/saveAppearance.ts @@ -1,12 +1,12 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveAppearance'(settings: { _id: string; value: any }[]): Promise; diff --git a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts index 2e3e643656493..9bf32697fae9a 100644 --- a/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts +++ b/apps/meteor/app/livechat/server/methods/saveBusinessHour.ts @@ -1,10 +1,10 @@ import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { businessHourManager } from '../business-hour'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveBusinessHour'(businessHourData: ILivechatBusinessHour): void; diff --git a/apps/meteor/app/livechat/server/methods/saveCustomField.ts b/apps/meteor/app/livechat/server/methods/saveCustomField.ts index 8a0435560dd5c..c83148ba8d299 100644 --- a/apps/meteor/app/livechat/server/methods/saveCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/saveCustomField.ts @@ -1,12 +1,12 @@ import type { ILivechatCustomField } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatCustomField } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveCustomField'( diff --git a/apps/meteor/app/livechat/server/methods/saveDepartment.ts b/apps/meteor/app/livechat/server/methods/saveDepartment.ts index 971c2189f9a78..b4833523ab3f9 100644 --- a/apps/meteor/app/livechat/server/methods/saveDepartment.ts +++ b/apps/meteor/app/livechat/server/methods/saveDepartment.ts @@ -1,11 +1,11 @@ import type { ILivechatDepartment } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveDepartment': ( diff --git a/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts b/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts index 73a4718699263..42ee521713c00 100644 --- a/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts +++ b/apps/meteor/app/livechat/server/methods/saveDepartmentAgents.ts @@ -1,12 +1,12 @@ import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveDepartmentAgents'( diff --git a/apps/meteor/app/livechat/server/methods/saveInfo.ts b/apps/meteor/app/livechat/server/methods/saveInfo.ts index a057706fdc239..bb22c127effa5 100644 --- a/apps/meteor/app/livechat/server/methods/saveInfo.ts +++ b/apps/meteor/app/livechat/server/methods/saveInfo.ts @@ -1,6 +1,6 @@ import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat as LivechatTyped } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveInfo'( diff --git a/apps/meteor/app/livechat/server/methods/saveIntegration.ts b/apps/meteor/app/livechat/server/methods/saveIntegration.ts index de7461d08e100..0a4d82cfbcc14 100644 --- a/apps/meteor/app/livechat/server/methods/saveIntegration.ts +++ b/apps/meteor/app/livechat/server/methods/saveIntegration.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Settings } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { trim } from '../../../../lib/utils/stringUtils'; @@ -7,7 +7,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { notifyOnSettingChangedById } from '../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveIntegration'(values: Record): void; diff --git a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts index 48dea8175ea84..36fc3f775b9f2 100644 --- a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts +++ b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms, LivechatVisitors } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { UpdateResult } from 'mongodb'; @@ -7,7 +7,7 @@ import _ from 'underscore'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveSurveyFeedback'( diff --git a/apps/meteor/app/livechat/server/methods/saveTrigger.ts b/apps/meteor/app/livechat/server/methods/saveTrigger.ts index 37f78d081203d..850dead9a5898 100644 --- a/apps/meteor/app/livechat/server/methods/saveTrigger.ts +++ b/apps/meteor/app/livechat/server/methods/saveTrigger.ts @@ -1,13 +1,13 @@ import type { ILivechatTrigger } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatTrigger } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:saveTrigger'(trigger: ILivechatTrigger): boolean; diff --git a/apps/meteor/app/livechat/server/methods/searchAgent.ts b/apps/meteor/app/livechat/server/methods/searchAgent.ts index 2a69679f2a078..932eb51e89d66 100644 --- a/apps/meteor/app/livechat/server/methods/searchAgent.ts +++ b/apps/meteor/app/livechat/server/methods/searchAgent.ts @@ -1,12 +1,12 @@ import type { IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:searchAgent'(username: string): { _id: string; username?: string } | undefined; diff --git a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts index 15577abd76e30..0268207b2f6c5 100644 --- a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts +++ b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.ts @@ -5,9 +5,9 @@ import type { VideoAttachmentProps, IUpload, } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -21,7 +21,7 @@ interface ISendFileLivechatMessage { msgData?: { avatar?: string; emoji?: string; alias?: string; groupable?: boolean; msg?: string }; } -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { sendFileLivechatMessage( diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts index ad16dea23e4fa..6fac80397906f 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts @@ -1,6 +1,6 @@ import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -18,7 +18,7 @@ interface ISendMessageLivechat { agent?: ILivechatMessageAgent; } -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { sendMessageLivechat(message: ILivechatMessage, agent: ILivechatMessageAgent): boolean; diff --git a/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts b/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts index c3b5537f31be1..b620aa4341008 100644 --- a/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts +++ b/apps/meteor/app/livechat/server/methods/sendOfflineMessage.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import { Meteor } from 'meteor/meteor'; @@ -6,7 +6,7 @@ import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:sendOfflineMessage'(data: { name: string; email: string; message: string }): Promise; diff --git a/apps/meteor/app/livechat/server/methods/sendTranscript.ts b/apps/meteor/app/livechat/server/methods/sendTranscript.ts index 366a73c8bb0a7..4891f579926a1 100644 --- a/apps/meteor/app/livechat/server/methods/sendTranscript.ts +++ b/apps/meteor/app/livechat/server/methods/sendTranscript.ts @@ -1,6 +1,6 @@ import { Omnichannel } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatRooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -8,7 +8,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { RateLimiter } from '../../../lib/server'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:sendTranscript'(token: string, rid: string, email: string, subject: string): boolean; diff --git a/apps/meteor/app/livechat/server/methods/setCustomField.ts b/apps/meteor/app/livechat/server/methods/setCustomField.ts index fb07c2b29a4ee..6d3a2384cdd7c 100644 --- a/apps/meteor/app/livechat/server/methods/setCustomField.ts +++ b/apps/meteor/app/livechat/server/methods/setCustomField.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, LivechatCustomField, LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import type { UpdateResult, Document } from 'mongodb'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:setCustomField'(token: string, key: string, value: string, overwrite?: boolean): Promise; diff --git a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts index a14933ed8d47e..385f2d9890157 100644 --- a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts +++ b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, Messages, LivechatRooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -7,7 +7,7 @@ import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarn import { normalizeTransferredByData } from '../lib/Helper'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:setDepartmentForVisitor'({ diff --git a/apps/meteor/app/livechat/server/methods/setUpConnection.ts b/apps/meteor/app/livechat/server/methods/setUpConnection.ts index 788b2384c4d30..21ce09acaa229 100644 --- a/apps/meteor/app/livechat/server/methods/setUpConnection.ts +++ b/apps/meteor/app/livechat/server/methods/setUpConnection.ts @@ -1,11 +1,11 @@ import { UserStatus } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:setUpConnection'(data: { token: string }): void; diff --git a/apps/meteor/app/livechat/server/methods/takeInquiry.ts b/apps/meteor/app/livechat/server/methods/takeInquiry.ts index 30a5dabb57176..733cbd995208e 100644 --- a/apps/meteor/app/livechat/server/methods/takeInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/takeInquiry.ts @@ -1,13 +1,13 @@ import { Omnichannel } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; import { RoutingManager } from '../lib/RoutingManager'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:takeInquiry'( diff --git a/apps/meteor/app/livechat/server/methods/transfer.ts b/apps/meteor/app/livechat/server/methods/transfer.ts index 64a32c24638c3..e0516fa279817 100644 --- a/apps/meteor/app/livechat/server/methods/transfer.ts +++ b/apps/meteor/app/livechat/server/methods/transfer.ts @@ -1,7 +1,7 @@ import { Omnichannel } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { LivechatVisitors, LivechatRooms, Subscriptions, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -10,7 +10,7 @@ import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarn import { normalizeTransferredByData } from '../lib/Helper'; import { Livechat } from '../lib/LivechatTyped'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'livechat:transfer'(transferData: { diff --git a/apps/meteor/app/livechat/server/methods/webhookTest.ts b/apps/meteor/app/livechat/server/methods/webhookTest.ts index 12a4711d75b4b..68800e1a0616d 100644 --- a/apps/meteor/app/livechat/server/methods/webhookTest.ts +++ b/apps/meteor/app/livechat/server/methods/webhookTest.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { SystemLogger } from '../../../../server/lib/logger/system'; @@ -14,7 +14,7 @@ const postCatchError = async function (url: string, options?: Record; diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 2557fcdeb83d0..c7f88646158b9 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -1,5 +1,5 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../lib/callbacks'; @@ -8,8 +8,8 @@ import { normalizeMessageFileUpload } from '../../utils/server/functions/normali import { callbackLogger } from './lib/logger'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (isEditedMessage(message)) { return message; @@ -20,7 +20,7 @@ callbacks.add( } // only send the sms by SMS if it is a livechat room with SMS set to true - if (!(isOmnichannelRoom(room) && room.sms && room.v && room.v.token)) { + if (!(room.sms && room.v && room.v.token)) { return message; } diff --git a/apps/meteor/app/livechat/server/startup.ts b/apps/meteor/app/livechat/server/startup.ts index b61c85c4001ea..32cf01d2e6404 100644 --- a/apps/meteor/app/livechat/server/startup.ts +++ b/apps/meteor/app/livechat/server/startup.ts @@ -87,7 +87,7 @@ Meteor.startup(async () => { }); // Remove when accounts.onLogout is async - Accounts.onLogout(({ user }: { user: IUser }) => { + Accounts.onLogout(({ user }: { user?: IUser }) => { if (!user?.roles?.includes('livechat-agent') || user?.roles?.includes('bot')) { return; } diff --git a/apps/meteor/app/mail-messages/server/methods/sendMail.ts b/apps/meteor/app/mail-messages/server/methods/sendMail.ts index 158d271d5379a..cc6469544df8e 100644 --- a/apps/meteor/app/mail-messages/server/methods/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/methods/sendMail.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Mailer } from '../lib/Mailer'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'Mailer.sendMail'(from: string, subject: string, body: string, dryrun?: boolean, query?: string): any; diff --git a/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts b/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts index 24243cc98c2d9..e52677b5ee1aa 100644 --- a/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts +++ b/apps/meteor/app/mail-messages/server/methods/unsubscribe.ts @@ -1,11 +1,11 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { Mailer } from '../lib/Mailer'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'Mailer:unsubscribe'(_id: string, createdAt: string): Promise; diff --git a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts index 948187d089ae6..6d9a16539704e 100644 --- a/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts +++ b/apps/meteor/app/mentions/server/methods/getUserMentionsByChannel.ts @@ -1,12 +1,12 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Users, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../../authorization/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getUserMentionsByChannel(params: { roomId: string; options: { limit: number; sort: { ts: -1 | 1 } } }): IMessage[]; diff --git a/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts b/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts index 2ba0224a0c704..6ef1f5567a201 100644 --- a/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts +++ b/apps/meteor/app/message-mark-as-unread/server/unreadMessages.ts @@ -1,11 +1,11 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Subscriptions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import logger from './logger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { unreadMessages(firstUnreadMessage?: IMessage, room?: IRoom['_id']): void; diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index b0eab3f929d66..f691a775cb6a4 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -2,17 +2,16 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message } from '@rocket.chat/core-services'; import { isQuoteAttachment, isRegisterUser } from '@rocket.chat/core-typings'; import type { IMessage, MessageAttachment, MessageQuoteAttachment } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Rooms, Subscriptions, Users, ReadReceipts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { isTruthy } from '../../../lib/isTruthy'; -import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; -import { notifyOnRoomChangedById } from '../../lib/server/lib/notifyListener'; +import { notifyOnRoomChangedById, notifyOnMessageChange } from '../../lib/server/lib/notifyListener'; import { settings } from '../../settings/server'; import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; @@ -36,7 +35,7 @@ const recursiveRemove = (msg: MessageAttachment, deep = 1) => { const shouldAdd = (attachments: MessageAttachment[], attachment: MessageQuoteAttachment) => !attachments.some((_attachment) => isQuoteAttachment(_attachment) && _attachment.message_link === attachment.message_link); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { pinMessage(message: IMessage, pinnedAt?: Date): IMessage | null; @@ -227,7 +226,7 @@ Meteor.methods({ if (settings.get('Message_Read_Receipt_Store_Users')) { await ReadReceipts.setPinnedByMessageId(originalMessage._id, originalMessage.pinned); } - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: message._id, }); diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 4529efb63f6fb..36c67c1f40206 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -1,16 +1,15 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; -import { notifyOnRoomChangedById } from '../../lib/server/lib/notifyListener'; +import { notifyOnRoomChangedById, notifyOnMessageChange } from '../../lib/server/lib/notifyListener'; import { settings } from '../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { starMessage(message: Omit & { starred: boolean }): boolean; @@ -63,7 +62,7 @@ Meteor.methods({ await Messages.updateUserStarById(message._id, uid, message.starred); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: message._id, }); diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts index 5c16716720b04..bb95672603377 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/settings.ts @@ -230,12 +230,10 @@ export const addSettings = async function (name: string): Promise { await this.add(`SAML_Custom_${name}_button_label_color`, '#FFFFFF', { type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Label_Color', - alert: 'OAuth_button_colors_alert', }); await this.add(`SAML_Custom_${name}_button_color`, '#1d74f5', { type: 'string', i18nLabel: 'Accounts_OAuth_Custom_Button_Color', - alert: 'OAuth_button_colors_alert', }); }); diff --git a/apps/meteor/app/meteor-accounts-saml/server/methods/addSamlService.ts b/apps/meteor/app/meteor-accounts-saml/server/methods/addSamlService.ts index f952ce3b3fe3c..0b6b5a6c8f6ec 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/methods/addSamlService.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/methods/addSamlService.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { addSamlService } from '../lib/settings'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addSamlService(name: string): void; diff --git a/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts b/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts index 956426082d405..a7f9e87a93de9 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/methods/samlLogout.ts @@ -1,5 +1,5 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; @@ -25,7 +25,7 @@ function getSamlServiceProviderOptions(provider: string): IServiceProviderOption return providers.filter(samlProvider)[0]; } -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { samlLogout(provider: string): string | undefined; diff --git a/apps/meteor/app/models/client/models/Users.ts b/apps/meteor/app/models/client/models/Users.ts index 089486f3a20ca..26d333cc8bb1b 100644 --- a/apps/meteor/app/models/client/models/Users.ts +++ b/apps/meteor/app/models/client/models/Users.ts @@ -30,5 +30,13 @@ class UsersCollection extends Mongo.Collection { } } +Object.assign(Meteor.users, { + _connection: undefined, + findOneById: UsersCollection.prototype.findOneById, + isUserInRole: UsersCollection.prototype.isUserInRole, + findUsersInRoles: UsersCollection.prototype.findUsersInRoles, + remove: UsersCollection.prototype.remove, +}); + /** @deprecated */ -export const Users = new UsersCollection(); +export const Users = Meteor.users as UsersCollection; diff --git a/apps/meteor/app/notifications/server/lib/Notifications.ts b/apps/meteor/app/notifications/server/lib/Notifications.ts index ed4985777414c..3ddb611d76bd1 100644 --- a/apps/meteor/app/notifications/server/lib/Notifications.ts +++ b/apps/meteor/app/notifications/server/lib/Notifications.ts @@ -1,5 +1,5 @@ import { api } from '@rocket.chat/core-services'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { DDPCommon } from 'meteor/ddp-common'; import { Meteor } from 'meteor/meteor'; diff --git a/apps/meteor/app/notifications/server/lib/Presence.ts b/apps/meteor/app/notifications/server/lib/Presence.ts index 9955b687701f8..17f35a9d39ff1 100644 --- a/apps/meteor/app/notifications/server/lib/Presence.ts +++ b/apps/meteor/app/notifications/server/lib/Presence.ts @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; +import type { StreamerEvents } from '@rocket.chat/ddp-client'; import { Emitter } from '@rocket.chat/emitter'; -import type { StreamerEvents } from '@rocket.chat/ui-contexts'; import type { IPublication, IStreamerConstructor, Connection, IStreamer } from 'meteor/rocketchat:streamer'; type UserPresenceStreamProps = { diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.ts index 4556a5be7d9fd..3f41b1b01bea4 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/addOAuthApp.ts @@ -1,12 +1,12 @@ import type { IOAuthApps } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import type { OauthAppsAddParams } from '@rocket.chat/rest-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { methodDeprecationLogger } from '../../../../lib/server/lib/deprecationWarningLogger'; import { addOAuthApp } from '../functions/addOAuthApp'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addOAuthApp(application: OauthAppsAddParams): IOAuthApps; diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts index 0209f9d453b51..9c5cbf6fd9ae6 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/deleteOAuthApp.ts @@ -1,11 +1,11 @@ import type { IOAuthApps } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { OAuthAccessTokens, OAuthApps, OAuthAuthCodes } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteOAuthApp(applicationId: IOAuthApps['_id']): boolean; diff --git a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts index 525d0fa2d124b..f2daca1885c9a 100644 --- a/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts +++ b/apps/meteor/app/oauth2-server-config/server/admin/methods/updateOAuthApp.ts @@ -1,12 +1,12 @@ import type { IOAuthApps } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { OAuthApps, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { parseUriList } from '../functions/parseUriList'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { updateOAuthApp( diff --git a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts index a994b19dd1a4b..1b57c65b4bb7d 100644 --- a/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts +++ b/apps/meteor/app/otr/server/methods/deleteOldOTRMessages.ts @@ -1,9 +1,9 @@ import type { IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Subscriptions, ReadReceipts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteOldOTRMessages(roomId: IRoom['_id']): Promise; diff --git a/apps/meteor/app/otr/server/methods/sendSystemMessages.ts b/apps/meteor/app/otr/server/methods/sendSystemMessages.ts index a2abf0938b9e6..13abbd7511afd 100644 --- a/apps/meteor/app/otr/server/methods/sendSystemMessages.ts +++ b/apps/meteor/app/otr/server/methods/sendSystemMessages.ts @@ -1,7 +1,7 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { sendSystemMessages(rid: string, user: string | undefined, id: string): void; diff --git a/apps/meteor/app/otr/server/methods/updateOTRAck.ts b/apps/meteor/app/otr/server/methods/updateOTRAck.ts index a5a502ddf1a2d..4fbd182e9d273 100644 --- a/apps/meteor/app/otr/server/methods/updateOTRAck.ts +++ b/apps/meteor/app/otr/server/methods/updateOTRAck.ts @@ -1,9 +1,9 @@ import { api } from '@rocket.chat/core-services'; import type { IOTRMessage } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { updateOTRAck({ message, ack }: { message: IOTRMessage; ack: string }): void; diff --git a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts index a4376c709275d..a86ded6f24e57 100644 --- a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts +++ b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts @@ -1,6 +1,6 @@ import type { ISubscription } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Subscriptions } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { getUserNotificationPreference } from '../../../utils/server/getUserNoti const saveAudioNotificationValue = (subId: ISubscription['_id'], value: string) => value === 'default' ? Subscriptions.clearAudioNotificationValueById(subId) : Subscriptions.updateAudioNotificationValueById(subId, value); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { saveNotificationSettings( diff --git a/apps/meteor/app/push/server/methods.ts b/apps/meteor/app/push/server/methods.ts index 364a16cbdb5e3..1f1e261eccae0 100644 --- a/apps/meteor/app/push/server/methods.ts +++ b/apps/meteor/app/push/server/methods.ts @@ -1,7 +1,7 @@ import type { IAppsTokens } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { AppsTokens } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Accounts } from 'meteor/accounts-base'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -9,7 +9,7 @@ import { Meteor } from 'meteor/meteor'; import { logger } from './logger'; import { _matchToken } from './push'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'raix:push-update'(options: { diff --git a/apps/meteor/app/reactions/client/methods/setReaction.ts b/apps/meteor/app/reactions/client/methods/setReaction.ts index a38e6c156790f..ed15cda9ab8e0 100644 --- a/apps/meteor/app/reactions/client/methods/setReaction.ts +++ b/apps/meteor/app/reactions/client/methods/setReaction.ts @@ -1,5 +1,5 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/reactions/server/setReaction.ts b/apps/meteor/app/reactions/server/setReaction.ts index 896e5041bd615..e35103e9d3339 100644 --- a/apps/meteor/app/reactions/server/setReaction.ts +++ b/apps/meteor/app/reactions/server/setReaction.ts @@ -1,19 +1,18 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import { api } from '@rocket.chat/core-services'; import type { IMessage, IRoom, IUser } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, EmojiCustom, Rooms, Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; import { callbacks } from '../../../lib/callbacks'; import { i18n } from '../../../server/lib/i18n'; -import { broadcastMessageFromData } from '../../../server/modules/watchers/lib/messages'; import { canAccessRoomAsync } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { emoji } from '../../emoji/server'; import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; -import { notifyOnRoomChangedById } from '../../lib/server/lib/notifyListener'; +import { notifyOnRoomChangedById, notifyOnMessageChange } from '../../lib/server/lib/notifyListener'; const removeUserReaction = (message: IMessage, reaction: string, username: string) => { if (!message.reactions) { @@ -111,7 +110,7 @@ async function setReaction(room: IRoom, user: IUser, message: IMessage, reaction await Apps.self?.triggerEvent(AppEvents.IPostMessageReacted, message, user, reaction, isReacted); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: message._id, }); } @@ -140,7 +139,7 @@ export async function executeSetReaction(userId: string, reaction: string, messa return setReaction(room, user, message, reaction, shouldReact); } -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setReaction(reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): boolean | undefined; diff --git a/apps/meteor/app/search/server/methods.ts b/apps/meteor/app/search/server/methods.ts index 686b5b429e96c..cdb3b4180f7a6 100644 --- a/apps/meteor/app/search/server/methods.ts +++ b/apps/meteor/app/search/server/methods.ts @@ -1,12 +1,12 @@ import type { IMessageSearchProvider, IMessageSearchSuggestion, IRoom, IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { SearchLogger } from './logger/logger'; import type { IRawSearchResult, ISearchResult } from './model/ISearchResult'; import { searchProviderService, validationService } from './service'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'rocketchatSearch.getProvider'(): IMessageSearchProvider | undefined; diff --git a/apps/meteor/app/settings/server/SettingsRegistry.ts b/apps/meteor/app/settings/server/SettingsRegistry.ts index 443e38ce5d63e..e86a391ad8fa1 100644 --- a/apps/meteor/app/settings/server/SettingsRegistry.ts +++ b/apps/meteor/app/settings/server/SettingsRegistry.ts @@ -73,7 +73,7 @@ const compareSettingsIgnoringKeys = .filter((key) => !keys.includes(key as keyof ISetting)) .every((key) => isEqual(a[key as keyof ISetting], b[key as keyof ISetting])); -const compareSettings = compareSettingsIgnoringKeys([ +export const compareSettings = compareSettingsIgnoringKeys([ 'value', 'ts', 'createdAt', @@ -138,22 +138,17 @@ export class SettingsRegistry { const settingFromCodeOverwritten = overwriteSetting(settingFromCode); - const settingOverwrittenDefault = overrideSetting(settingFromCode); - const settingStored = this.store.getSetting(_id); - const settingStoredOverwritten = settingStored && overwriteSetting(settingStored); - const isOverwritten = settingFromCode !== settingFromCodeOverwritten || (settingStored && settingStored !== settingStoredOverwritten); - - const updatedSettingAfterApplyingOverwrite = isOverwritten ? settingFromCodeOverwritten : settingOverwrittenDefault; - try { validateSetting(settingFromCode._id, settingFromCode.type, settingFromCode.value); } catch (e) { IS_DEVELOPMENT && SystemLogger.error(`Invalid setting code ${_id}: ${(e as Error).message}`); } + const isOverwritten = settingFromCode !== settingFromCodeOverwritten || (settingStored && settingStored !== settingStoredOverwritten); + const { _id: _, ...settingProps } = settingFromCodeOverwritten; if (settingStored && !compareSettings(settingStored, settingFromCodeOverwritten)) { @@ -171,9 +166,7 @@ export class SettingsRegistry { })(); await this.saveUpdatedSetting(_id, updatedProps, removedKeys); - - this.store.set(updatedSettingAfterApplyingOverwrite); - + this.store.set(settingFromCodeOverwritten); return; } @@ -183,8 +176,7 @@ export class SettingsRegistry { const removedKeys = Object.keys(settingStored).filter((key) => !['_updatedAt'].includes(key) && !overwrittenKeys.includes(key)); await this.saveUpdatedSetting(_id, settingProps, removedKeys); - - this.store.set(updatedSettingAfterApplyingOverwrite); + this.store.set(settingFromCodeOverwritten); } return; } @@ -198,9 +190,13 @@ export class SettingsRegistry { return; } - await this.model.insertOne(updatedSettingAfterApplyingOverwrite); // no need to emit unless we remove the oplog + const settingOverwrittenDefault = overrideSetting(settingFromCode); + + const setting = isOverwritten ? settingFromCodeOverwritten : settingOverwrittenDefault; + + await this.model.insertOne(setting); // no need to emit unless we remove the oplog - this.store.set(updatedSettingAfterApplyingOverwrite); + this.store.set(setting); } /* diff --git a/apps/meteor/app/settings/server/functions/settings.mocks.ts b/apps/meteor/app/settings/server/functions/settings.mocks.ts index 9cd409ba0b837..fb31c3021b1b3 100644 --- a/apps/meteor/app/settings/server/functions/settings.mocks.ts +++ b/apps/meteor/app/settings/server/functions/settings.mocks.ts @@ -9,6 +9,12 @@ type Dictionary = { class SettingsClass { settings: ICachedSettings; + private delay = 0; + + setDelay(delay: number): void { + this.delay = delay; + } + find(): any[] { return []; } @@ -65,22 +71,41 @@ class SettingsClass { throw new Error('Invalid upsert'); } - // console.log(query, data); - this.data.set(query._id, data); - - // Can't import before the mock command on end of this file! - // eslint-disable-next-line @typescript-eslint/no-var-requires - this.settings.set(data); + if (this.delay) { + setTimeout(() => { + // console.log(query, data); + this.data.set(query._id, data); + + // Can't import before the mock command on end of this file! + // eslint-disable-next-line @typescript-eslint/no-var-requires + this.settings.set(data); + }, this.delay); + } else { + this.data.set(query._id, data); + // Can't import before the mock command on end of this file! + // eslint-disable-next-line @typescript-eslint/no-var-requires + this.settings.set(data); + } this.upsertCalls++; } + findOneAndUpdate({ _id }: { _id: string }, value: any, options?: any) { + this.updateOne({ _id }, value, options); + return { value: this.findOne({ _id }) }; + } + updateValueById(id: string, value: any): void { this.data.set(id, { ...this.data.get(id), value }); - // Can't import before the mock command on end of this file! // eslint-disable-next-line @typescript-eslint/no-var-requires - this.settings.set(this.data.get(id) as ISetting); + if (this.delay) { + setTimeout(() => { + this.settings.set(this.data.get(id) as ISetting); + }, this.delay); + } else { + this.settings.set(this.data.get(id) as ISetting); + } } } diff --git a/apps/meteor/app/slackbridge/server/SlackAdapter.js b/apps/meteor/app/slackbridge/server/SlackAdapter.js index 78d48deb49935..0263d5369a4c5 100644 --- a/apps/meteor/app/slackbridge/server/SlackAdapter.js +++ b/apps/meteor/app/slackbridge/server/SlackAdapter.js @@ -1341,7 +1341,7 @@ export default class SlackAdapter { const user = (await this.rocket.findUser(member)) || (await this.rocket.addUser(member)); if (user) { slackLogger.debug('Adding user to room', user.username, rid); - await addUserToRoom(rid, user, null, true); + await addUserToRoom(rid, user, null, { skipSystemMessage: true }); } } } diff --git a/apps/meteor/app/slackbridge/server/removeChannelLinks.ts b/apps/meteor/app/slackbridge/server/removeChannelLinks.ts index 1a4e6f6a00960..2c89e71f9635a 100644 --- a/apps/meteor/app/slackbridge/server/removeChannelLinks.ts +++ b/apps/meteor/app/slackbridge/server/removeChannelLinks.ts @@ -1,11 +1,11 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { settings } from '../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { removeSlackBridgeChannelLinks(): { message: string; params: unknown[] }; diff --git a/apps/meteor/app/statistics/server/methods/getStatistics.ts b/apps/meteor/app/statistics/server/methods/getStatistics.ts index c7eb79b5b9852..a804ef83584e3 100644 --- a/apps/meteor/app/statistics/server/methods/getStatistics.ts +++ b/apps/meteor/app/statistics/server/methods/getStatistics.ts @@ -1,10 +1,10 @@ import type { IStats } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { getLastStatistics } from '../functions/getLastStatistics'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getStatistics(refresh?: boolean): IStats; diff --git a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts index ab1fa182599bb..179cb5ec12b77 100644 --- a/apps/meteor/app/threads/server/hooks/aftersavemessage.ts +++ b/apps/meteor/app/threads/server/hooks/aftersavemessage.ts @@ -4,7 +4,7 @@ import { Messages } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages'; +import { notifyOnMessageChange } from '../../../lib/server/lib/notifyListener'; import { updateThreadUsersSubscriptions, getMentions } from '../../../lib/server/lib/notifyUsersOnMessage'; import { sendMessageNotifications } from '../../../lib/server/lib/sendNotificationsOnMessage'; import { settings } from '../../../settings/server'; @@ -62,7 +62,7 @@ export async function processThreads(message: IMessage, room: IRoom) { await notifyUsersOnReply(message, replies); await metaData(message, parentMessage, replies); await notification(message, room, replies); - void broadcastMessageFromData({ + void notifyOnMessageChange({ id: message.tmid, }); diff --git a/apps/meteor/app/threads/server/methods/followMessage.ts b/apps/meteor/app/threads/server/methods/followMessage.ts index 05650d0ad2efd..1790e0607a623 100644 --- a/apps/meteor/app/threads/server/methods/followMessage.ts +++ b/apps/meteor/app/threads/server/methods/followMessage.ts @@ -1,7 +1,7 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -10,7 +10,7 @@ import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; import { follow } from '../functions'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { followMessage(message: { mid: IMessage['_id'] }): false | undefined; diff --git a/apps/meteor/app/threads/server/methods/getThreadMessages.ts b/apps/meteor/app/threads/server/methods/getThreadMessages.ts index d6c2d65ff4d46..8ae31130df1b0 100644 --- a/apps/meteor/app/threads/server/methods/getThreadMessages.ts +++ b/apps/meteor/app/threads/server/methods/getThreadMessages.ts @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; @@ -8,7 +8,7 @@ import { canAccessRoomAsync } from '../../../authorization/server'; import { settings } from '../../../settings/server'; import { readThread } from '../functions'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getThreadMessages(params: { tmid: IMessage['_id']; limit?: number; skip?: number }): Promise; diff --git a/apps/meteor/app/threads/server/methods/getThreadsList.ts b/apps/meteor/app/threads/server/methods/getThreadsList.ts index 5a33dc16280b6..c51449ff2d4d3 100644 --- a/apps/meteor/app/threads/server/methods/getThreadsList.ts +++ b/apps/meteor/app/threads/server/methods/getThreadsList.ts @@ -1,6 +1,6 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages, Rooms } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { canAccessRoomAsync } from '../../../authorization/server'; @@ -8,7 +8,7 @@ import { settings } from '../../../settings/server'; const MAX_LIMIT = 100; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getThreadsList(params: { rid: IRoom['_id']; limit?: number; skip?: number }): IMessage[]; diff --git a/apps/meteor/app/threads/server/methods/unfollowMessage.ts b/apps/meteor/app/threads/server/methods/unfollowMessage.ts index afc9206b038f9..6371f40af6cb4 100644 --- a/apps/meteor/app/threads/server/methods/unfollowMessage.ts +++ b/apps/meteor/app/threads/server/methods/unfollowMessage.ts @@ -1,7 +1,7 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import type { IMessage } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Messages } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -10,7 +10,7 @@ import { RateLimiter } from '../../../lib/server'; import { settings } from '../../../settings/server'; import { unfollow } from '../functions'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { unfollowMessage(message: { mid: IMessage['_id'] }): false | undefined; diff --git a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts index 545e1e73342d4..602a0eddb8ce7 100644 --- a/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts +++ b/apps/meteor/app/ui-cached-collection/client/models/CachedCollection.ts @@ -1,5 +1,5 @@ +import type { StreamNames } from '@rocket.chat/ddp-client'; import { Emitter } from '@rocket.chat/emitter'; -import type { StreamNames } from '@rocket.chat/ui-contexts'; import localforage from 'localforage'; import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; diff --git a/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts b/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts index 845731e2450ad..b031151058417 100644 --- a/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts +++ b/apps/meteor/app/ui-cached-collection/client/models/CachedCollectionManager.ts @@ -41,12 +41,12 @@ class CachedCollectionManager extends Emitter<{ reconnect: void; login: string | } }); + Accounts.onLogin(() => { + this.emit('login', Meteor.userId()); + }); Tracker.autorun(() => { const uid = Meteor.userId(); this.logged = uid !== null; - if (this.logged) { - this.emit('login', uid); - } }); } diff --git a/apps/meteor/app/ui-utils/lib/MessageTypes.ts b/apps/meteor/app/ui-utils/lib/MessageTypes.ts index a4f77d10cbf7b..c108fe55f1684 100644 --- a/apps/meteor/app/ui-utils/lib/MessageTypes.ts +++ b/apps/meteor/app/ui-utils/lib/MessageTypes.ts @@ -5,8 +5,6 @@ export type MessageType = { id: MessageTypesValues; system?: boolean; /* deprecated */ - render?: (message: IMessage) => string; - /* deprecated */ template?: (message: IMessage) => unknown; message: TranslationKey; data?: (message: IMessage) => Record; diff --git a/apps/meteor/app/ui-utils/server/Message.ts b/apps/meteor/app/ui-utils/server/Message.ts index 18cf842b19936..06ae59238b429 100644 --- a/apps/meteor/app/ui-utils/server/Message.ts +++ b/apps/meteor/app/ui-utils/server/Message.ts @@ -11,9 +11,6 @@ export const Message = { parse(msg: IMessage, language: string) { const messageType = MessageTypes.getType(msg); if (messageType) { - if (messageType.render) { - return messageType.render(msg); - } if (messageType.template) { // Render message return; diff --git a/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts b/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts index f58fa9551f4ca..416bc6f678ede 100644 --- a/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/deleteCustomUserStatus.ts @@ -1,11 +1,11 @@ import { api } from '@rocket.chat/core-services'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { CustomUserStatus } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { deleteCustomUserStatus(userStatusID: string): Promise; diff --git a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts index 5aa80627d5624..911a69854b955 100644 --- a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts +++ b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts @@ -1,9 +1,9 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { getStatusText } from '../../../lib/server/functions/getStatusText'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getUserStatusText(userId: string): Promise; diff --git a/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts b/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts index b6a98895bc2c2..6e034f0306797 100644 --- a/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/insertOrUpdateUserStatus.ts @@ -1,14 +1,14 @@ import { api } from '@rocket.chat/core-services'; import type { ICustomUserStatus } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import type { InsertionModel } from '@rocket.chat/model-typings'; import { CustomUserStatus } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { trim } from '../../../../lib/utils/stringUtils'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { insertOrUpdateUserStatus(userStatusData: { diff --git a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts index 3a962121d65c7..d9a58f8b8c720 100644 --- a/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/listCustomUserStatus.ts @@ -1,9 +1,9 @@ import type { ICustomUserStatus } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { CustomUserStatus } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { listCustomUserStatus(): ICustomUserStatus[]; diff --git a/apps/meteor/app/user-status/server/methods/setUserStatus.ts b/apps/meteor/app/user-status/server/methods/setUserStatus.ts index 32db93e722fbe..0b40e7e37246b 100644 --- a/apps/meteor/app/user-status/server/methods/setUserStatus.ts +++ b/apps/meteor/app/user-status/server/methods/setUserStatus.ts @@ -1,6 +1,6 @@ import { Presence } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -8,7 +8,7 @@ import { RateLimiter } from '../../../lib/server'; import { setStatusText } from '../../../lib/server/functions/setStatusText'; import { settings } from '../../../settings/server'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { setUserStatus(statusType: IUser['status'], statusText: IUser['statusText']): void; diff --git a/apps/meteor/app/utils/client/lib/SDKClient.ts b/apps/meteor/app/utils/client/lib/SDKClient.ts index c174f9125f49f..3c7e43c85f7c0 100644 --- a/apps/meteor/app/utils/client/lib/SDKClient.ts +++ b/apps/meteor/app/utils/client/lib/SDKClient.ts @@ -1,15 +1,12 @@ import type { RestClientInterface } from '@rocket.chat/api-client'; -import type { SDK } from '@rocket.chat/ddp-client/src/DDPSDK'; -import type { ClientStream } from '@rocket.chat/ddp-client/src/types/ClientStream'; -import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; +import type { SDK, ClientStream, StreamKeys, StreamNames, StreamerCallbackArgs, ServerMethods } from '@rocket.chat/ddp-client'; import { Emitter } from '@rocket.chat/emitter'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { DDPCommon } from 'meteor/ddp-common'; import { Meteor } from 'meteor/meteor'; import { APIClient } from './RestApiClient'; -declare module '@rocket.chat/ddp-client/src/DDPSDK' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface SDK { stream>( @@ -138,6 +135,12 @@ const createStreamManager = () => { const streams = new Map(); + Accounts.onLogout(() => { + streams.forEach((stream) => { + stream.unsubList.forEach((stop) => stop()); + }); + }); + Meteor.connection._stream.on('message', (rawMsg: string) => { const msg = DDPCommon.parseDDP(rawMsg); if (!isChangedCollectionPayload(msg)) { @@ -169,7 +172,6 @@ const createStreamManager = () => { const stop = (): void => { streamProxy.off(eventLiteral, proxyCallback); - // If someone is still listening, don't unsubscribe if (streamProxy.has(eventLiteral)) { return; @@ -182,11 +184,15 @@ const createStreamManager = () => { }; const stream = streams.get(eventLiteral) || createNewMeteorStream(name, key, args); + stream.unsubList.add(stop); if (!streams.has(eventLiteral)) { streams.set(eventLiteral, stream); } - stream.error(() => stop()); + + stream.error(() => { + stream.unsubList.forEach((stop) => stop()); + }); return { id: '', diff --git a/apps/meteor/app/utils/lib/i18n.ts b/apps/meteor/app/utils/lib/i18n.ts index 9d3fbc59d245e..b69fe6b305139 100644 --- a/apps/meteor/app/utils/lib/i18n.ts +++ b/apps/meteor/app/utils/lib/i18n.ts @@ -9,11 +9,11 @@ export const i18n = i18next.use(sprintf); export const addSprinfToI18n = function (t: (typeof i18n)['t']) { return function (key: string, ...replaces: any): string { if (replaces[0] === undefined) { - return t(key, ...replaces); + return t(key); } if (isObject(replaces[0]) && !Array.isArray(replaces[0])) { - return t(key, ...replaces); + return t(key, replaces[0]); } return t(key, { diff --git a/apps/meteor/app/utils/lib/slashCommand.ts b/apps/meteor/app/utils/lib/slashCommand.ts index f4e2cc1b359ac..47149807bbd8e 100644 --- a/apps/meteor/app/utils/lib/slashCommand.ts +++ b/apps/meteor/app/utils/lib/slashCommand.ts @@ -126,7 +126,7 @@ export const slashCommands = { }, }; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { slashCommand(params: { cmd: string; params: string; msg: IMessage; triggerId: string }): unknown; diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index ef7c8e5717c96..faf0e8f47de6c 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.11.0-develop" + "version": "6.12.0-develop" } diff --git a/apps/meteor/app/utils/server/slashCommand.ts b/apps/meteor/app/utils/server/slashCommand.ts index 74f2c5716cc4c..dc85fee9b6718 100644 --- a/apps/meteor/app/utils/server/slashCommand.ts +++ b/apps/meteor/app/utils/server/slashCommand.ts @@ -1,4 +1,4 @@ -import type { ServerMethods } from '@rocket.chat/ui-contexts'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Meteor } from 'meteor/meteor'; import { slashCommands } from '../lib/slashCommand'; diff --git a/apps/meteor/app/version-check/server/methods/banner_dismiss.ts b/apps/meteor/app/version-check/server/methods/banner_dismiss.ts index 5ffebcfbbd5a7..a3bf337626f92 100644 --- a/apps/meteor/app/version-check/server/methods/banner_dismiss.ts +++ b/apps/meteor/app/version-check/server/methods/banner_dismiss.ts @@ -1,10 +1,10 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Users } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { notifyOnUserChange } from '../../../lib/server/lib/notifyListener'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { 'banner/dismiss'({ id }: { id: string }): void; diff --git a/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts b/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts index e2c9a9ddc3246..2c39736490913 100644 --- a/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts +++ b/apps/meteor/app/webdav/server/methods/addWebdavAccount.ts @@ -1,14 +1,14 @@ import { api } from '@rocket.chat/core-services'; import type { IWebdavAccountPayload } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { WebdavAccounts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { WebdavClientAdapter } from '../lib/webdavClientAdapter'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { addWebdavAccount(formData: IWebdavAccountPayload): boolean; diff --git a/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts b/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts index 346362935d875..38aeb0442c5c6 100644 --- a/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts +++ b/apps/meteor/app/webdav/server/methods/getFileFromWebdav.ts @@ -1,15 +1,15 @@ import type { IWebdavAccount, IWebdavNode } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { WebdavAccounts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; import { WebdavClientAdapter } from '../lib/webdavClientAdapter'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention - export interface ServerMethods { + interface ServerMethods { getFileFromWebdav(accountId: IWebdavAccount['_id'], file: IWebdavNode): Promise<{ success: boolean; data: Uint8Array }>; } } diff --git a/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts b/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts index f4d9441819287..66e924b719a2a 100644 --- a/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts +++ b/apps/meteor/app/webdav/server/methods/getWebdavFileList.ts @@ -1,13 +1,13 @@ import type { IWebdavAccount, IWebdavNode } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { WebdavAccounts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; import { WebdavClientAdapter } from '../lib/webdavClientAdapter'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getWebdavFileList(accountId: IWebdavAccount['_id'], path: string): { success: boolean; data: IWebdavNode[] }; diff --git a/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts b/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts index 1e01937a0e52f..34077babf276c 100644 --- a/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts +++ b/apps/meteor/app/webdav/server/methods/getWebdavFilePreview.ts @@ -1,13 +1,13 @@ import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { WebdavAccounts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { createClient } from 'webdav'; import { settings } from '../../../settings/server'; import { getWebdavCredentials } from '../lib/getWebdavCredentials'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { getWebdavFilePreview(accountId: IWebdavAccount['_id'], path: string): { success: true; data: ArrayBuffer } | undefined; diff --git a/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts b/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts index df6102297cc2e..a68a14e29e40b 100644 --- a/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts +++ b/apps/meteor/app/webdav/server/methods/removeWebdavAccount.ts @@ -1,14 +1,14 @@ import { api } from '@rocket.chat/core-services'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { WebdavAccounts } from '@rocket.chat/models'; -import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import type { DeleteResult } from 'mongodb'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { removeWebdavAccount(accountId: IWebdavAccount['_id']): DeleteResult; diff --git a/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts b/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts index 8a2e1badd27d2..97bcf46322161 100644 --- a/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts +++ b/apps/meteor/app/webdav/server/methods/uploadFileToWebdav.ts @@ -1,7 +1,8 @@ import { MeteorError } from '@rocket.chat/core-services'; import type { IWebdavAccount } from '@rocket.chat/core-typings'; +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Logger } from '@rocket.chat/logger'; -import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../../settings/server'; @@ -9,7 +10,7 @@ import { uploadFileToWebdav } from '../lib/uploadFileToWebdav'; const logger = new Logger('WebDAV_Upload'); -declare module '@rocket.chat/ui-contexts' { +declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention interface ServerMethods { uploadFileToWebdav( diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx index fd4498f5fb8e3..531ff8a74b663 100644 --- a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx +++ b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx @@ -1,5 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { ComponentProps } from 'react'; import React, { memo, useState } from 'react'; import GenericMenu from '../../../components/GenericMenu/GenericMenu'; @@ -8,9 +9,9 @@ import { useHandleMenuAction } from '../../../components/GenericMenu/hooks/useHa import UserMenuButton from './UserMenuButton'; import { useUserMenu } from './hooks/useUserMenu'; -type UserMenuProps = { user: IUser; className?: string }; +type UserMenuProps = { user: IUser } & Omit, 'sections' | 'items' | 'title'>; -const UserMenu = function UserMenu({ user }: UserMenuProps) { +const UserMenu = function UserMenu({ user, ...props }: UserMenuProps) { const t = useTranslation(); const [isOpen, setIsOpen] = useState(false); @@ -21,6 +22,7 @@ const UserMenu = function UserMenu({ user }: UserMenuProps) { return ( {t('Topic')} - + } + /> {t('Displayed_next_to_name')} @@ -243,7 +246,7 @@ const CreateDiscussion = ({ onClose, defaultParentRoom, parentMessageId, nameSug - diff --git a/apps/meteor/client/components/FilterByText.tsx b/apps/meteor/client/components/FilterByText.tsx index 1aeeb29a0a57a..5c5a3d599e2f9 100644 --- a/apps/meteor/client/components/FilterByText.tsx +++ b/apps/meteor/client/components/FilterByText.tsx @@ -1,26 +1,13 @@ -import { Box, Icon, TextInput, Button, Margins } from '@rocket.chat/fuselage'; +import { Box, Icon, TextInput, Margins } from '@rocket.chat/fuselage'; import { useAutoFocus, useMergedRefs } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactNode, ChangeEvent, FormEvent } from 'react'; -import React, { forwardRef, memo, useCallback, useEffect, useState } from 'react'; +import type { ChangeEvent, FormEvent, HTMLAttributes } from 'react'; +import React, { forwardRef, memo, useCallback, useState } from 'react'; -type FilterByTextCommonProps = { - children?: ReactNode | undefined; - placeholder?: string; - onChange: (filter: { text: string }) => void; +type FilterByTextProps = { + onChange: (filter: string) => void; shouldAutoFocus?: boolean; -}; - -type FilterByTextPropsWithButton = FilterByTextCommonProps & { - displayButton: true; - textButton: string; - onButtonClick: () => void; -}; - -type FilterByTextProps = FilterByTextCommonProps | FilterByTextPropsWithButton; - -const isFilterByTextPropsWithButton = (props: any): props is FilterByTextPropsWithButton => - 'displayButton' in props && props.displayButton === true; +} & Omit, 'is' | 'onChange'>; const FilterByText = forwardRef(function FilterByText( { placeholder, onChange: setFilter, shouldAutoFocus = false, children, ...props }, @@ -31,13 +18,10 @@ const FilterByText = forwardRef(function Fi const autoFocusRef = useAutoFocus(shouldAutoFocus); const mergedRefs = useMergedRefs(ref, autoFocusRef); - const handleInputChange = useCallback((event: ChangeEvent) => { + const handleInputChange = (event: ChangeEvent) => { setText(event.currentTarget.value); - }, []); - - useEffect(() => { - setFilter({ text }); - }, [setFilter, text]); + setFilter(event.currentTarget.value); + }; const handleFormSubmit = useCallback((event: FormEvent) => { event.preventDefault(); @@ -47,6 +31,7 @@ const FilterByText = forwardRef(function Fi } @@ -57,13 +42,7 @@ const FilterByText = forwardRef(function Fi aria-label={placeholder ?? t('Search')} /> - {isFilterByTextPropsWithButton(props) ? ( - - ) : ( - children && {children} - )} + {children && {children}} ); }); diff --git a/apps/meteor/client/components/GenericMenu/GenericMenu.spec.tsx b/apps/meteor/client/components/GenericMenu/GenericMenu.spec.tsx new file mode 100644 index 0000000000000..99e62bac1a607 --- /dev/null +++ b/apps/meteor/client/components/GenericMenu/GenericMenu.spec.tsx @@ -0,0 +1,60 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import GenericMenu from './GenericMenu'; + +const mockedFunction = jest.fn(); +const regular = { + items: [ + { + id: 'edit', + content: 'Edit', + icon: 'pencil' as const, + onClick: mockedFunction, + }, + ], +}; +const danger = { + items: [ + { + id: 'delete', + content: 'Delete', + icon: 'trash' as const, + onClick: () => null, + variant: 'danger', + }, + ], +}; + +const sections = [regular, danger]; + +describe('Room Actions Menu', () => { + it('should render kebab menu with the list content', async () => { + render(); + + userEvent.click(screen.getByRole('button')); + + expect(await screen.findByText('Edit')).toBeInTheDocument(); + expect(await screen.findByText('Delete')).toBeInTheDocument(); + }); + + it('should have two different sections, regular and danger', async () => { + render(); + + userEvent.click(screen.getByRole('button')); + + expect(screen.getAllByRole('presentation')).toHaveLength(2); + expect(screen.getByRole('separator')).toBeInTheDocument(); + }); + + it('should call the action when item clicked', async () => { + render(); + + userEvent.click(screen.getByRole('button')); + userEvent.click(screen.getAllByRole('menuitem')[0]); + + expect(mockedFunction).toHaveBeenCalled(); + }); +}); diff --git a/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx index 44feedf861154..c01a64d708a02 100644 --- a/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx @@ -13,6 +13,7 @@ export type GenericMenuItemProps = { description?: ReactNode; gap?: boolean; tooltip?: string; + variant?: string; }; const GenericMenuItem = ({ icon, content, addon, status, gap, tooltip }: GenericMenuItemProps) => ( diff --git a/apps/meteor/client/components/GenericModal/GenericModal.spec.tsx b/apps/meteor/client/components/GenericModal/GenericModal.spec.tsx new file mode 100644 index 0000000000000..0ef7235729c48 --- /dev/null +++ b/apps/meteor/client/components/GenericModal/GenericModal.spec.tsx @@ -0,0 +1,87 @@ +import { useSetModal } from '@rocket.chat/ui-contexts'; +import { act, screen } from '@testing-library/react'; +import { renderHook } from '@testing-library/react-hooks'; +import userEvent from '@testing-library/user-event'; +import type { ReactElement } from 'react'; +import React, { Suspense } from 'react'; + +import ModalProviderWithRegion from '../../providers/ModalProvider/ModalProviderWithRegion'; +import GenericModal from './GenericModal'; + +import '@testing-library/jest-dom'; + +const renderModal = (modalElement: ReactElement) => { + const { + result: { current: setModal }, + } = renderHook(() => useSetModal(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + act(() => { + setModal(modalElement); + }); + + return { setModal }; +}; + +describe('callbacks', () => { + it('should call onClose callback when dismissed', async () => { + const handleClose = jest.fn(); + + renderModal(); + + expect(await screen.findByRole('heading', { name: 'Modal', exact: true })).toBeInTheDocument(); + + userEvent.keyboard('{Escape}'); + + expect(screen.queryByRole('heading', { name: 'Modal', exact: true })).not.toBeInTheDocument(); + + expect(handleClose).toHaveBeenCalled(); + }); + + it('should NOT call onClose callback when confirmed', async () => { + const handleConfirm = jest.fn(); + const handleClose = jest.fn(); + + const { setModal } = renderModal(); + + expect(await screen.findByRole('heading', { name: 'Modal', exact: true })).toBeInTheDocument(); + + userEvent.click(screen.getByRole('button', { name: 'Ok', exact: true })); + + expect(handleConfirm).toHaveBeenCalled(); + + act(() => { + setModal(null); + }); + + expect(screen.queryByRole('heading', { name: 'Modal', exact: true })).not.toBeInTheDocument(); + + expect(handleClose).not.toHaveBeenCalled(); + }); + + it('should NOT call onClose callback when cancelled', async () => { + const handleCancel = jest.fn(); + const handleClose = jest.fn(); + + const { setModal } = renderModal(); + + expect(await screen.findByRole('heading', { name: 'Modal', exact: true })).toBeInTheDocument(); + + userEvent.click(screen.getByRole('button', { name: 'Cancel', exact: true })); + + expect(handleCancel).toHaveBeenCalled(); + + act(() => { + setModal(null); + }); + + expect(screen.queryByRole('heading', { name: 'Modal', exact: true })).not.toBeInTheDocument(); + + expect(handleClose).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index 914928d4d423d..d371e1ff4ef2c 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -1,9 +1,9 @@ import { Button, Modal } from '@rocket.chat/fuselage'; -import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import { useEffectEvent, useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement, ReactNode, ComponentPropsWithoutRef } from 'react'; -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import type { RequiredModalProps } from './withDoNotAskAgain'; import { withDoNotAskAgain } from './withDoNotAskAgain'; @@ -78,6 +78,31 @@ const GenericModal = ({ const t = useTranslation(); const genericModalId = useUniqueId(); + const dismissedRef = useRef(true); + + const handleConfirm = useEffectEvent(() => { + dismissedRef.current = false; + onConfirm?.(); + }); + + const handleCancel = useEffectEvent(() => { + dismissedRef.current = false; + onCancel?.(); + }); + + const handleCloseButtonClick = useEffectEvent(() => { + dismissedRef.current = true; + onClose?.(); + }); + + useEffect( + () => () => { + if (!dismissedRef.current) return; + onClose?.(); + }, + [onClose], + ); + return ( @@ -86,7 +111,7 @@ const GenericModal = ({ {tagline && {tagline}} {title ?? t('Are_you_sure')} - + {children} @@ -94,7 +119,7 @@ const GenericModal = ({ {annotation && !dontAskAgain && {annotation}} {onCancel && ( - )} @@ -104,7 +129,7 @@ const GenericModal = ({ )} {!wrapperFunction && onConfirm && ( - )} diff --git a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.spec.tsx b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.spec.tsx index 9a2e7eac4c45a..fb97b0132f850 100644 --- a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.spec.tsx +++ b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.spec.tsx @@ -4,21 +4,22 @@ import '@testing-library/jest-dom/extend-expect'; import { createRenteionPolicySettingsMock as createMock } from '../../../tests/mocks/client/mockRetentionPolicySettings'; import { createFakeRoom } from '../../../tests/mocks/data'; -import { setDate } from '../../../tests/mocks/mockDate'; import RetentionPolicyCallout from './RetentionPolicyCallout'; jest.useFakeTimers(); +beforeEach(() => { + jest.setSystemTime(new Date(2024, 5, 1, 0, 0, 0)); +}); + describe('RetentionPolicyCallout', () => { it('Should render callout if settings are valid', () => { - setDate(); const fakeRoom = createFakeRoom({ t: 'c' }); render(, { wrapper: createMock({ appliesToChannels: true, TTLChannels: 60000 }) }); expect(screen.getByRole('alert')).toHaveTextContent('a minute June 1, 2024, 12:30 AM'); }); it('Should not render callout if settings are invalid', () => { - setDate(); const fakeRoom = createFakeRoom({ t: 'c' }); render(, { wrapper: createMock({ appliesToChannels: true, TTLChannels: 60000, advancedPrecisionCron: '* * * 12 *', advancedPrecision: true }), diff --git a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx index 401448ceb3966..7c028fb5c876a 100644 --- a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx @@ -52,6 +52,8 @@ const CloseChatModal = ({ } = useForm(); const commentRequired = useSetting('Livechat_request_comment_when_closing_conversation') as boolean; + const alwaysSendTranscript = useSetting('Livechat_transcript_send_always'); + const customSubject = useSetting('Livechat_transcript_email_subject'); const [tagRequired, setTagRequired] = useState(false); const tags = watch('tags'); @@ -65,7 +67,7 @@ const CloseChatModal = ({ const transcriptPDFPermission = usePermission('request-pdf-transcript'); const transcriptEmailPermission = usePermission('send-omnichannel-chat-transcript'); - const canSendTranscriptEmail = transcriptEmailPermission && visitorEmail; + const canSendTranscriptEmail = transcriptEmailPermission && visitorEmail && !alwaysSendTranscript; const canSendTranscriptPDF = transcriptPDFPermission && hasLicense; const canSendTranscript = canSendTranscriptEmail || canSendTranscriptPDF; @@ -77,7 +79,7 @@ const CloseChatModal = ({ ({ comment, tags, transcriptPDF, transcriptEmail, subject }): void => { const preferences = { omnichannelTranscriptPDF: !!transcriptPDF, - omnichannelTranscriptEmail: !!transcriptEmail, + omnichannelTranscriptEmail: alwaysSendTranscript ? true : !!transcriptEmail, }; const requestData = transcriptEmail && visitorEmail ? { email: visitorEmail, subject } : undefined; @@ -97,7 +99,7 @@ const CloseChatModal = ({ onConfirm(comment, tags, preferences, requestData); } }, - [commentRequired, tagRequired, visitorEmail, errors, setError, t, onConfirm], + [commentRequired, tagRequired, visitorEmail, errors, setError, t, onConfirm, alwaysSendTranscript], ); const cannotSubmit = useMemo(() => { @@ -132,9 +134,9 @@ const CloseChatModal = ({ dispatchToastMessage({ type: 'error', message: t('Customer_without_registered_email') }); return; } - setValue('subject', subject || t('Transcript_of_your_livechat_conversation')); + setValue('subject', subject || customSubject || t('Transcript_of_your_livechat_conversation')); } - }, [transcriptEmail, setValue, visitorEmail, subject, t]); + }, [transcriptEmail, setValue, visitorEmail, subject, t, customSubject]); if (commentRequired || tagRequired || canSendTranscript) { return ( diff --git a/apps/meteor/client/components/Page/PageScrollableContent.tsx b/apps/meteor/client/components/Page/PageScrollableContent.tsx index c3ac6869f277f..f8c3bb5ba54b3 100644 --- a/apps/meteor/client/components/Page/PageScrollableContent.tsx +++ b/apps/meteor/client/components/Page/PageScrollableContent.tsx @@ -1,4 +1,3 @@ -import { css } from '@rocket.chat/css-in-js'; import type { Scrollable } from '@rocket.chat/fuselage'; import { Box } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; @@ -26,17 +25,7 @@ const PageScrollableContent = forwardRef - + ); diff --git a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx index 1f12f29ee13b3..3f61f421035c8 100644 --- a/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx +++ b/apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx @@ -1,4 +1,4 @@ -import { AutoComplete, Option, Box, Chip, Options } from '@rocket.chat/fuselage'; +import { AutoComplete, Option, Box, Chip } from '@rocket.chat/fuselage'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useEndpoint } from '@rocket.chat/ui-contexts'; @@ -46,7 +46,7 @@ const UserAutoComplete = ({ value, onChange, ...props }: UserAutoCompleteProps): )} renderItem={({ value, label, ...props }): ReactElement => ( - - {((isSuccess && data?.monitors.length > 0) || queryHasChanged) && setText(text)} />} + {((isSuccess && data?.monitors.length > 0) || queryHasChanged) && } {isLoading && ( {headers} diff --git a/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx b/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx index d550118a5017d..01b4bfe903748 100644 --- a/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx +++ b/apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx @@ -84,7 +84,7 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { return ( <> - {((isSuccess && data?.sla.length > 0) || queryHasChanged) && setFilter(text)} />} + {((isSuccess && data?.sla.length > 0) || queryHasChanged) && } {isLoading && ( {headers} diff --git a/apps/meteor/client/omnichannel/tags/TagsTable.tsx b/apps/meteor/client/omnichannel/tags/TagsTable.tsx index 3b9757134bedd..a4b31cd4755c3 100644 --- a/apps/meteor/client/omnichannel/tags/TagsTable.tsx +++ b/apps/meteor/client/omnichannel/tags/TagsTable.tsx @@ -70,7 +70,7 @@ const TagsTable = () => { return ( <> - {((isSuccess && data?.tags.length > 0) || queryHasChanged) && setFilter(text)} />} + {((isSuccess && data?.tags.length > 0) || queryHasChanged) && } {isLoading && ( {headers} diff --git a/apps/meteor/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/client/omnichannel/units/UnitsTable.tsx index fe95bc90d8a20..93734acb8de0d 100644 --- a/apps/meteor/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/client/omnichannel/units/UnitsTable.tsx @@ -69,7 +69,7 @@ const UnitsTable = () => { return ( <> - {((isSuccess && data?.units.length > 0) || queryHasChanged) && setFilter(text)} />} + {((isSuccess && data?.units.length > 0) || queryHasChanged) && } {isLoading && ( {headers} diff --git a/apps/meteor/client/polyfills/hoverTouchClick.ts b/apps/meteor/client/polyfills/hoverTouchClick.ts deleted file mode 100644 index 53706a45fb33b..0000000000000 --- a/apps/meteor/client/polyfills/hoverTouchClick.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as domEvents from '../lib/utils/domEvents'; -import { isIOsDevice } from '../lib/utils/isIOsDevice'; - -((): void => { - if (!isIOsDevice || !window.matchMedia('(hover: none)').matches) { - return; - } - - domEvents.delegate({ - parent: document.body, - eventName: 'touchend', - elementSelector: 'a:hover', - listener: (_, element): void => { - domEvents.triggerClick(element); - }, - }); -})(); diff --git a/apps/meteor/client/polyfills/index.ts b/apps/meteor/client/polyfills/index.ts index bc91265b04ba9..be470f261e265 100644 --- a/apps/meteor/client/polyfills/index.ts +++ b/apps/meteor/client/polyfills/index.ts @@ -3,5 +3,4 @@ import 'url-polyfill'; import './childNodeRemove'; import './cssVars'; import './customEventPolyfill'; -import './hoverTouchClick'; import './promiseFinally'; diff --git a/apps/meteor/client/portals/ModalPortal.tsx b/apps/meteor/client/portals/ModalPortal.tsx index d7c9ae9caa2d8..6b2210d569260 100644 --- a/apps/meteor/client/portals/ModalPortal.tsx +++ b/apps/meteor/client/portals/ModalPortal.tsx @@ -1,18 +1,32 @@ -import type { ReactElement, ReactNode } from 'react'; -import React, { memo, useEffect, useState } from 'react'; +import type { ReactNode } from 'react'; +import { memo } from 'react'; import { createPortal } from 'react-dom'; -import { createAnchor } from '../lib/utils/createAnchor'; -import { deleteAnchor } from '../lib/utils/deleteAnchor'; +const createModalRoot = (): HTMLElement => { + const id = 'modal-root'; + const existing = document.getElementById(id); + + if (existing) return existing; + + const newOne = document.createElement('div'); + newOne.id = id; + document.body.append(newOne); + + return newOne; +}; + +let modalRoot: HTMLElement | null = null; type ModalPortalProps = { children?: ReactNode; }; -const ModalPortal = ({ children }: ModalPortalProps): ReactElement => { - const [modalRoot] = useState(() => createAnchor('modal-root')); - useEffect(() => (): void => deleteAnchor(modalRoot), [modalRoot]); - return <>{createPortal(children, modalRoot)}; +const ModalPortal = ({ children }: ModalPortalProps) => { + if (!modalRoot) { + modalRoot = createModalRoot(); + } + + return createPortal(children, modalRoot); }; export default memo(ModalPortal); diff --git a/apps/meteor/client/providers/ModalProvider/ModalProvider.spec.tsx b/apps/meteor/client/providers/ModalProvider/ModalProvider.spec.tsx index f779333374561..ea062c3248071 100644 --- a/apps/meteor/client/providers/ModalProvider/ModalProvider.spec.tsx +++ b/apps/meteor/client/providers/ModalProvider/ModalProvider.spec.tsx @@ -1,115 +1,138 @@ -// import type { IMessage } from '@rocket.chat/core-typings'; -import { Emitter } from '@rocket.chat/emitter'; import { useSetModal } from '@rocket.chat/ui-contexts'; -import { render, screen } from '@testing-library/react'; -import { expect } from 'chai'; -import type { ReactNode } from 'react'; -import React, { Suspense, createContext, useContext, useEffect } from 'react'; +import { act, render, screen } from '@testing-library/react'; +import type { ForwardedRef, ReactElement } from 'react'; +import React, { Suspense, createContext, createRef, forwardRef, useContext, useImperativeHandle } from 'react'; import GenericModal from '../../components/GenericModal'; import { imperativeModal } from '../../lib/imperativeModal'; import ModalRegion from '../../views/modal/ModalRegion'; import ModalProvider from './ModalProvider'; import ModalProviderWithRegion from './ModalProviderWithRegion'; +import '@testing-library/jest-dom'; -const TestContext = createContext({ title: 'default' }); -const emitter = new Emitter(); +const renderWithSuspense = (ui: ReactElement) => + render(ui, { + wrapper: ({ children }) => {children}, + }); -const TestModal = ({ emitterEvent, modalFunc }: { emitterEvent: string; modalFunc?: () => ReactNode }) => { - const setModal = useSetModal(); - const { title } = useContext(TestContext); +describe('via useSetModal', () => { + const ModalTitleContext = createContext('default'); - useEffect(() => { - emitter.on(emitterEvent, () => { - setModal(modalFunc || undefined}>); - }); - }, [emitterEvent, setModal, title, modalFunc]); + type ModalOpenerAPI = { open: () => void }; - return <>; -}; + const ModalOpener = forwardRef((_: unknown, ref: ForwardedRef) => { + const setModal = useSetModal(); + const title = useContext(ModalTitleContext); + useImperativeHandle(ref, () => ({ + open: () => { + setModal(); + }, + })); + + return null; + }); -describe('Modal Provider', () => { it('should render a modal', async () => { - render( - + const modalOpenerRef = createRef(); + + renderWithSuspense( + + + , + ); + + act(() => { + modalOpenerRef.current?.open(); + }); + + expect(await screen.findByRole('dialog', { name: 'default' })).toBeInTheDocument(); + }); + + it('should render a modal that consumes a context', async () => { + const modalOpenerRef = createRef(); + + renderWithSuspense( + - + - , + , ); - emitter.emit('open'); - expect(await screen.findByText('default')).to.exist; + act(() => { + modalOpenerRef.current?.open(); + }); + + expect(await screen.findByRole('dialog', { name: 'title from context' })).toBeInTheDocument(); }); - it('should render a modal that is passed as a function', async () => { - render( - + it('should render a modal in another region', async () => { + const modalOpener1Ref = createRef(); + const modalOpener2Ref = createRef(); + + renderWithSuspense( + - undefined} />} /> + - , + + + + + + , ); - emitter.emit('open'); - expect(await screen.findByText('function modal')).to.exist; + + act(() => { + modalOpener1Ref.current?.open(); + }); + + expect(await screen.findByRole('dialog', { name: 'modal1' })).toBeInTheDocument(); + + act(() => { + modalOpener2Ref.current?.open(); + }); + + expect(await screen.findByRole('dialog', { name: 'modal2' })).toBeInTheDocument(); }); +}); + +describe('via imperativeModal', () => { + it('should render a modal through imperative modal', async () => { + renderWithSuspense( + + + , + ); - it('should render a modal through imperative modal', () => { - async () => { - render( - - - - - , - ); - - const { close } = imperativeModal.open({ + act(() => { + imperativeModal.open({ component: GenericModal, - props: { title: 'imperativeModal' }, + props: { title: 'imperativeModal', open: true }, }); + }); - expect(await screen.findByText('imperativeModal')).to.exist; + expect(await screen.findByRole('dialog', { name: 'imperativeModal' })).toBeInTheDocument(); - close(); + act(() => { + imperativeModal.close(); + }); - expect(screen.queryByText('imperativeModal')).to.not.exist; - }; + expect(screen.queryByText('imperativeModal')).not.toBeInTheDocument(); }); it('should not render a modal if no corresponding region exists', async () => { // ModalProviderWithRegion will always have a region identifier set // and imperativeModal will only render modals in the default region (e.g no region identifier) - render( - - - , - ); - - imperativeModal.open({ - component: GenericModal, - props: { title: 'imperativeModal' }, - }); - expect(screen.queryByText('imperativeModal')).to.not.exist; - }); + renderWithSuspense(); - it('should render a modal in another region', () => { - render( - - - - - - - - - - , - ); + act(() => { + imperativeModal.open({ + component: GenericModal, + props: { title: 'imperativeModal', open: true }, + }); + }); - emitter.emit('openModal1'); - expect(screen.getByText('modal1')).to.exist; - emitter.emit('openModal2'); - expect(screen.getByText('modal2')).to.exist; + expect(screen.queryByRole('dialog', { name: 'imperativeModal' })).not.toBeInTheDocument(); }); }); diff --git a/apps/meteor/client/providers/ModalProvider/ModalProvider.tsx b/apps/meteor/client/providers/ModalProvider/ModalProvider.tsx index 6c3f1026bc517..27092ea602b6a 100644 --- a/apps/meteor/client/providers/ModalProvider/ModalProvider.tsx +++ b/apps/meteor/client/providers/ModalProvider/ModalProvider.tsx @@ -33,7 +33,7 @@ const ModalProvider = ({ children, region }: ModalProviderProps) => { }, region, }), - [currentModal, region, setModal], + [currentModal?.node, currentModal?.region, region, setModal], ); return ; diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index 8ba6e699b2b10..590c5f20da57c 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -1,3 +1,5 @@ +import type { RoomType, RoomRouteData } from '@rocket.chat/core-typings'; +import { RouterContext } from '@rocket.chat/ui-contexts'; import type { RouterContextValue, RouteName, @@ -6,15 +8,15 @@ import type { SearchParameters, To, RouteObject, + LocationSearch, } from '@rocket.chat/ui-contexts'; -import { RouterContext } from '@rocket.chat/ui-contexts'; -import type { LocationSearch } from '@rocket.chat/ui-contexts/src/RouterContext'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Tracker } from 'meteor/tracker'; import type { ReactNode } from 'react'; import React from 'react'; import { appLayout } from '../lib/appLayout'; +import { roomCoordinator } from '../lib/rooms/roomCoordinator'; import { queueMicrotask } from '../lib/utils/queueMicrotask'; const subscribers = new Set<() => void>(); @@ -195,6 +197,9 @@ export const router: RouterContextValue = { defineRoutes, getRoutes, subscribeToRoutesChange, + getRoomRoute(roomType: RoomType, routeData: RoomRouteData) { + return { path: roomCoordinator.getRouteLink(roomType, routeData) || '/' }; + }, }; type RouterProviderProps = { diff --git a/apps/meteor/client/providers/ServerProvider.tsx b/apps/meteor/client/providers/ServerProvider.tsx index 53ac92287f784..7bf442671f08d 100644 --- a/apps/meteor/client/providers/ServerProvider.tsx +++ b/apps/meteor/client/providers/ServerProvider.tsx @@ -1,14 +1,14 @@ import type { Serialized } from '@rocket.chat/core-typings'; -import type { Method, PathFor, OperationParams, OperationResult, UrlParams, PathPattern } from '@rocket.chat/rest-typings'; import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn, StreamerCallbackArgs, - UploadResult, StreamNames, StreamKeys, -} from '@rocket.chat/ui-contexts'; +} from '@rocket.chat/ddp-client'; +import type { Method, PathFor, OperationParams, OperationResult, UrlParams, PathPattern } from '@rocket.chat/rest-typings'; +import type { UploadResult } from '@rocket.chat/ui-contexts'; import { ServerContext } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { compile } from 'path-to-regexp'; diff --git a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx b/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx index f9ec077e9e430..cc7cdfbe7761a 100644 --- a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx +++ b/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx @@ -8,6 +8,7 @@ import React, { memo, useMemo } from 'react'; import { RoomIcon } from '../../components/RoomIcon'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; +import { isIOsDevice } from '../../lib/utils/isIOsDevice'; import { useOmnichannelPriorities } from '../../omnichannel/hooks/useOmnichannelPriorities'; import RoomMenu from '../RoomMenu'; import { OmnichannelBadges } from '../badges/OmnichannelBadges'; @@ -195,6 +196,7 @@ function SideBarItemTemplateWithData({ avatar={AvatarTemplate && } actions={actions} menu={ + !isIOsDevice && !isAnonymous && (!isQueued || (isQueued && isPriorityEnabled)) && ((): ReactElement => ( diff --git a/apps/meteor/client/sidebar/RoomMenu.tsx b/apps/meteor/client/sidebar/RoomMenu.tsx index 8df55bd5d3594..06b1352d28031 100644 --- a/apps/meteor/client/sidebar/RoomMenu.tsx +++ b/apps/meteor/client/sidebar/RoomMenu.tsx @@ -200,10 +200,14 @@ const RoomMenu = ({ const menuOptions = useMemo( () => ({ ...(!hideDefaultOptions && { - hideRoom: { - label: { label: t('Hide'), icon: 'eye-off' }, - action: handleHide, - }, + ...(isOmnichannelRoom + ? {} + : { + hideRoom: { + label: { label: t('Hide'), icon: 'eye-off' }, + action: handleHide, + }, + }), toggleRead: { label: { label: isUnread ? t('Mark_read') : t('Mark_unread'), icon: 'flag' }, action: handleToggleRead, diff --git a/apps/meteor/client/sidebar/footer/SidebarFooterDefault.tsx b/apps/meteor/client/sidebar/footer/SidebarFooterDefault.tsx index fbf987fa78af2..3d1c17dcebbe1 100644 --- a/apps/meteor/client/sidebar/footer/SidebarFooterDefault.tsx +++ b/apps/meteor/client/sidebar/footer/SidebarFooterDefault.tsx @@ -1,7 +1,7 @@ import { css } from '@rocket.chat/css-in-js'; import { Box, SidebarDivider, Palette, SidebarFooter as Footer } from '@rocket.chat/fuselage'; import { useSetting } from '@rocket.chat/ui-contexts'; -import { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode'; +import { useThemeMode } from '@rocket.chat/ui-theming'; import type { ReactElement } from 'react'; import React from 'react'; diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx index c45d300dc4c7d..27ad314b2ab8e 100644 --- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx +++ b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx @@ -12,6 +12,8 @@ import { FieldError, FieldHint, FieldDescription, + Accordion, + AccordionItem, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; @@ -210,7 +212,7 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal - + {t('Name')} @@ -276,90 +278,99 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')} - - - {t('Federation_Matrix_Federated')} - ( - + + + + + {t('Security_and_permissions')} + + + + {t('Federation_Matrix_Federated')} + ( + + )} /> - )} - /> - - {t(getFederationHintKey(federatedModule, federationEnabled))} - - - - {t('Encrypted')} - ( - + {t(getFederationHintKey(federatedModule, federationEnabled))} + + + + {t('Encrypted')} + ( + + )} /> - )} - /> - - {getEncryptedHint({ isPrivate, broadcast, encrypted })} - - - - {t('Read_only')} - ( - + {getEncryptedHint({ isPrivate, broadcast, encrypted })} + + + + {t('Read_only')} + ( + + )} /> - )} - /> - - - {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')} - - - - - {t('Broadcast')} - ( - + + {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')} + + + + + {t('Broadcast')} + ( + + )} /> - )} - /> - - {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}} - - + + {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}} + + + + diff --git a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx index b56bb003aa9e7..ad1abdcbf399b 100644 --- a/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx +++ b/apps/meteor/client/sidebar/header/CreateTeam/CreateTeamModal.tsx @@ -12,6 +12,8 @@ import { FieldError, FieldDescription, FieldHint, + Accordion, + AccordionItem, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { @@ -169,7 +171,7 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement => {t('Teams_new_description')} - + {t('Teams_New_Name_Label')} @@ -229,62 +231,77 @@ const CreateTeamModal = ({ onClose }: { onClose: () => void }): ReactElement => {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')} - - - {t('Teams_New_Read_only_Label')} - ( - + + + + + {t('Security_and_permissions')} + + + + {t('Teams_New_Encrypted_Label')} + ( + + )} /> - )} - /> - - - {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'team' }) : t('Anyone_can_send_new_messages')} - - - - - {t('Teams_New_Encrypted_Label')} - ( - + {getEncryptedHint({ isPrivate, broadcast, encrypted })} + + + + {t('Teams_New_Read_only_Label')} + ( + + )} /> - )} - /> - - {getEncryptedHint({ isPrivate, broadcast, encrypted })} - - - - {t('Teams_New_Broadcast_Label')} - ( - - )} - /> - - {broadcast && {t('Teams_New_Broadcast_Description')}} - - + + + {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'team' }) : t('Anyone_can_send_new_messages')} + + + + + {t('Teams_New_Broadcast_Label')} + ( + + )} + /> + + {broadcast && {t('Teams_New_Broadcast_Description')}} + + + + diff --git a/apps/meteor/client/sidebarv2/RoomList/SideBarItemTemplateWithData.tsx b/apps/meteor/client/sidebarv2/RoomList/SideBarItemTemplateWithData.tsx index 60444540f6fd0..4eaba8cc37f04 100644 --- a/apps/meteor/client/sidebarv2/RoomList/SideBarItemTemplateWithData.tsx +++ b/apps/meteor/client/sidebarv2/RoomList/SideBarItemTemplateWithData.tsx @@ -8,6 +8,7 @@ import React, { memo, useMemo } from 'react'; import { RoomIcon } from '../../components/RoomIcon'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; +import { isIOsDevice } from '../../lib/utils/isIOsDevice'; import { useOmnichannelPriorities } from '../../omnichannel/hooks/useOmnichannelPriorities'; import RoomMenu from '../RoomMenu'; import { OmnichannelBadges } from '../badges/OmnichannelBadges'; @@ -195,6 +196,7 @@ const SideBarItemTemplateWithData = ({ avatar={AvatarTemplate && } actions={actions} menu={ + !isIOsDevice && !isAnonymous && (!isQueued || (isQueued && isPriorityEnabled)) && ((): ReactElement => ( diff --git a/apps/meteor/client/sidebarv2/footer/SidebarFooterDefault.tsx b/apps/meteor/client/sidebarv2/footer/SidebarFooterDefault.tsx index fbf987fa78af2..3d1c17dcebbe1 100644 --- a/apps/meteor/client/sidebarv2/footer/SidebarFooterDefault.tsx +++ b/apps/meteor/client/sidebarv2/footer/SidebarFooterDefault.tsx @@ -1,7 +1,7 @@ import { css } from '@rocket.chat/css-in-js'; import { Box, SidebarDivider, Palette, SidebarFooter as Footer } from '@rocket.chat/fuselage'; import { useSetting } from '@rocket.chat/ui-contexts'; -import { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode'; +import { useThemeMode } from '@rocket.chat/ui-theming'; import type { ReactElement } from 'react'; import React from 'react'; diff --git a/apps/meteor/client/sidebarv2/header/CreateChannelModal.tsx b/apps/meteor/client/sidebarv2/header/CreateChannelModal.tsx index a395f784b70a4..38a1df5a5ec8a 100644 --- a/apps/meteor/client/sidebarv2/header/CreateChannelModal.tsx +++ b/apps/meteor/client/sidebarv2/header/CreateChannelModal.tsx @@ -12,6 +12,8 @@ import { FieldError, FieldHint, FieldDescription, + Accordion, + AccordionItem, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; @@ -210,7 +212,7 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal - + {t('Name')} @@ -276,90 +278,99 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')} - - - {t('Federation_Matrix_Federated')} - ( - + + + + + {t('Security_and_permissions')} + + + + {t('Federation_Matrix_Federated')} + ( + + )} /> - )} - /> - - {t(getFederationHintKey(federatedModule, federationEnabled))} - - - - {t('Encrypted')} - ( - + {t(getFederationHintKey(federatedModule, federationEnabled))} + + + + {t('Encrypted')} + ( + + )} /> - )} - /> - - {getEncryptedHint({ isPrivate, broadcast, encrypted })} - - - - {t('Read_only')} - ( - + {getEncryptedHint({ isPrivate, broadcast, encrypted })} + + + + {t('Read_only')} + ( + + )} /> - )} - /> - - - {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')} - - - - - {t('Broadcast')} - ( - + + {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')} + + + + + {t('Broadcast')} + ( + + )} /> - )} - /> - - {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}} - - + + {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}} + + + + diff --git a/apps/meteor/client/sidebarv2/header/CreateTeamModal.tsx b/apps/meteor/client/sidebarv2/header/CreateTeamModal.tsx index fbeaf869d7bd9..d8f84a4a53308 100644 --- a/apps/meteor/client/sidebarv2/header/CreateTeamModal.tsx +++ b/apps/meteor/client/sidebarv2/header/CreateTeamModal.tsx @@ -1,4 +1,4 @@ -import type { SidepanelItems } from '@rocket.chat/core-typings'; +import type { SidepanelItem } from '@rocket.chat/core-typings'; import { Box, Button, @@ -13,6 +13,8 @@ import { FieldError, FieldDescription, FieldHint, + Accordion, + AccordionItem, } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { @@ -130,9 +132,9 @@ const CreateTeamModal = ({ onClose }: CreateTeamModalProps) => { showChannels, showDiscussions, }: CreateTeamModalInputs): Promise => { - const sidepanelItems = [showChannels && 'channel', showDiscussions && 'discussion'].filter(Boolean) as [ - SidepanelItems, - SidepanelItems?, + const sidepanelItems = [showChannels && 'channels', showDiscussions && 'discussions'].filter(Boolean) as [ + SidepanelItem, + SidepanelItem?, ]; const params = { name, @@ -186,7 +188,7 @@ const CreateTeamModal = ({ onClose }: CreateTeamModalProps) => { {t('Teams_new_description')} - + {t('Teams_New_Name_Label')} @@ -246,102 +248,120 @@ const CreateTeamModal = ({ onClose }: CreateTeamModalProps) => { {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')} - - - {t('Teams_New_Read_only_Label')} - ( - + + + + + + {t('Navigation')} + + + + {t('Channels')} + ( + + )} /> - )} - /> - - - {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'team' }) : t('Anyone_can_send_new_messages')} - - - - - {t('Teams_New_Encrypted_Label')} - ( - + {t('Show_channels_description')} + + + + {t('Discussions')} + ( + + )} /> - )} - /> - - {getEncryptedHint({ isPrivate, broadcast, encrypted })} - - - - {t('Teams_New_Broadcast_Label')} - ( - - )} - /> - - {broadcast && {t('Teams_New_Broadcast_Description')}} - + + {t('Show_discussions_description')} + - - - {t('Channels')} - ( - + {t('Security_and_permissions')} + + + + {t('Teams_New_Encrypted_Label')} + ( + + )} /> - )} - /> - - {t('Show_channels_description')} - - - - - {t('Discussions')} - ( - + {getEncryptedHint({ isPrivate, broadcast, encrypted })} + + + + {t('Teams_New_Read_only_Label')} + ( + + )} /> - )} - /> - - {t('Show_discussions_description')} - - + + + {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'team' }) : t('Anyone_can_send_new_messages')} + + + + + {t('Teams_New_Broadcast_Label')} + ( + + )} + /> + + {broadcast && {t('Teams_New_Broadcast_Description')}} + + + + diff --git a/apps/meteor/client/sidebarv2/header/SearchSection.tsx b/apps/meteor/client/sidebarv2/header/SearchSection.tsx index 66d8eb65371fe..c1f3f8cf8c216 100644 --- a/apps/meteor/client/sidebarv2/header/SearchSection.tsx +++ b/apps/meteor/client/sidebarv2/header/SearchSection.tsx @@ -72,7 +72,7 @@ const SearchSection = () => { { }, order: 5, group: 'menu', + disabled({ message }) { + return isE2EEMessage(message); + }, }); }); diff --git a/apps/meteor/client/startup/actionButtons/permalinkStar.ts b/apps/meteor/client/startup/actionButtons/permalinkStar.ts index bf6deede9b107..a1ee8d79fc449 100644 --- a/apps/meteor/client/startup/actionButtons/permalinkStar.ts +++ b/apps/meteor/client/startup/actionButtons/permalinkStar.ts @@ -1,3 +1,4 @@ +import { isE2EEMessage } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { MessageAction } from '../../../app/ui-utils/client'; @@ -32,5 +33,8 @@ Meteor.startup(() => { }, order: 10, group: 'menu', + disabled({ message }) { + return isE2EEMessage(message); + }, }); }); diff --git a/apps/meteor/client/startup/startup.ts b/apps/meteor/client/startup/startup.ts index 2541529a1aef8..45c102a72bf49 100644 --- a/apps/meteor/client/startup/startup.ts +++ b/apps/meteor/client/startup/startup.ts @@ -28,10 +28,15 @@ Meteor.startup(() => { removeLocalUserData(); return; } + if (!Meteor.status().connected) { return; } + if (Meteor.loggingIn()) { + return; + } + const user = await synchronizeUserData(uid); if (!user) { return; diff --git a/apps/meteor/client/stories/contexts/ServerContextMock.tsx b/apps/meteor/client/stories/contexts/ServerContextMock.tsx index 4a23d172ced2c..2c9f835b35a59 100644 --- a/apps/meteor/client/stories/contexts/ServerContextMock.tsx +++ b/apps/meteor/client/stories/contexts/ServerContextMock.tsx @@ -1,6 +1,7 @@ import type { Serialized } from '@rocket.chat/core-typings'; +import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from '@rocket.chat/ddp-client'; import type { Method, OperationParams, OperationResult, PathFor, PathPattern } from '@rocket.chat/rest-typings'; -import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn, UploadResult } from '@rocket.chat/ui-contexts'; +import type { UploadResult } from '@rocket.chat/ui-contexts'; import { ServerContext } from '@rocket.chat/ui-contexts'; import { action } from '@storybook/addon-actions'; import type { ContextType, ReactElement, ReactNode } from 'react'; diff --git a/apps/meteor/client/uikit/hooks/useModalContextValue.ts b/apps/meteor/client/uikit/hooks/useModalContextValue.ts index e10a59902b6b5..4a0932c8e1e73 100644 --- a/apps/meteor/client/uikit/hooks/useModalContextValue.ts +++ b/apps/meteor/client/uikit/hooks/useModalContextValue.ts @@ -14,6 +14,7 @@ type UseModalContextValueParams = { blockId?: string | undefined; }; }; + errors?: { [field: string]: string }[] | { [field: string]: string }; updateValues: Dispatch<{ actionId: string; payload: { @@ -25,7 +26,7 @@ type UseModalContextValueParams = { type UseModalContextValueReturn = ContextType; -export const useModalContextValue = ({ view, values, updateValues }: UseModalContextValueParams): UseModalContextValueReturn => { +export const useModalContextValue = ({ view, errors, values, updateValues }: UseModalContextValueParams): UseModalContextValueReturn => { const actionManager = useUiKitActionManager(); const emitInteraction = useMemo(() => actionManager.emitInteraction.bind(actionManager), [actionManager]); @@ -62,6 +63,7 @@ export const useModalContextValue = ({ view, values, updateValues }: UseModalCon }); }, ...view, + errors, values, viewId: view.id, }; diff --git a/apps/meteor/client/views/account/accessibility/hooks/useAcessibilityPreferencesValues.ts b/apps/meteor/client/views/account/accessibility/hooks/useAcessibilityPreferencesValues.ts index 339cdcff7dd9b..9620e4286eb5f 100644 --- a/apps/meteor/client/views/account/accessibility/hooks/useAcessibilityPreferencesValues.ts +++ b/apps/meteor/client/views/account/accessibility/hooks/useAcessibilityPreferencesValues.ts @@ -1,6 +1,6 @@ +import type { ThemePreference } from '@rocket.chat/core-typings'; import type { FontSize } from '@rocket.chat/rest-typings'; import { useUserPreference } from '@rocket.chat/ui-contexts'; -import type { ThemePreference } from '@rocket.chat/ui-theming/src/types/themes'; export type AccessibilityPreferencesData = { themeAppearence?: ThemePreference; diff --git a/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx b/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx index 9abec01df1d05..8489b3cb4b8b9 100644 --- a/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx +++ b/apps/meteor/client/views/account/omnichannel/OmnichannelPreferencesPage.tsx @@ -1,5 +1,5 @@ import { ButtonGroup, Button, Box, Accordion } from '@rocket.chat/fuselage'; -import { useToastMessageDispatch, useTranslation, useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useTranslation, useEndpoint, useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; import { useForm, FormProvider } from 'react-hook-form'; @@ -17,12 +17,17 @@ const OmnichannelPreferencesPage = (): ReactElement => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); + const alwaysSendEmailTranscript = useSetting('Livechat_transcript_send_always'); const omnichannelTranscriptPDF = useUserPreference('omnichannelTranscriptPDF') ?? false; const omnichannelTranscriptEmail = useUserPreference('omnichannelTranscriptEmail') ?? false; const omnichannelHideConversationAfterClosing = useUserPreference('omnichannelHideConversationAfterClosing') ?? true; const methods = useForm({ - defaultValues: { omnichannelTranscriptPDF, omnichannelTranscriptEmail, omnichannelHideConversationAfterClosing }, + defaultValues: { + omnichannelTranscriptPDF, + omnichannelTranscriptEmail: alwaysSendEmailTranscript || omnichannelTranscriptEmail, + omnichannelHideConversationAfterClosing, + }, }); const { diff --git a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx index 11bf6634a0e9d..a003861c8d57b 100644 --- a/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx +++ b/apps/meteor/client/views/account/omnichannel/PreferencesConversationTranscript.tsx @@ -1,6 +1,6 @@ import { Accordion, Box, Field, FieldGroup, FieldLabel, FieldRow, FieldHint, Tag, ToggleSwitch } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useTranslation, usePermission } from '@rocket.chat/ui-contexts'; +import { useTranslation, usePermission, useSetting } from '@rocket.chat/ui-contexts'; import React from 'react'; import { useFormContext } from 'react-hook-form'; @@ -12,8 +12,10 @@ const PreferencesConversationTranscript = () => { const { register } = useFormContext(); const hasLicense = useHasLicenseModule('livechat-enterprise'); + const alwaysSendEmailTranscript = useSetting('Livechat_transcript_send_always'); const canSendTranscriptPDF = usePermission('request-pdf-transcript'); - const canSendTranscriptEmail = usePermission('send-omnichannel-chat-transcript'); + const canSendTranscriptEmailPermission = usePermission('send-omnichannel-chat-transcript'); + const canSendTranscriptEmail = canSendTranscriptEmailPermission && !alwaysSendEmailTranscript; const cantSendTranscriptPDF = !canSendTranscriptPDF || !hasLicense; const omnichannelTranscriptPDF = useUniqueId(); @@ -42,7 +44,7 @@ const PreferencesConversationTranscript = () => { {t('Omnichannel_transcript_email')} - {!canSendTranscriptEmail && ( + {!canSendTranscriptEmailPermission && ( {t('No_permission')} diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx index abfa438d9ced8..2bd795c3ccab7 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx @@ -66,7 +66,7 @@ const CustomEmoji = ({ onClick, reload }: CustomEmojiProps) => { return ( <> - setText(text)} /> + {isLoading && ( {headers} diff --git a/apps/meteor/client/views/admin/customSounds/CustomSoundsTable/CustomSoundsTable.tsx b/apps/meteor/client/views/admin/customSounds/CustomSoundsTable/CustomSoundsTable.tsx index 58b1c4ea7b8af..5e8bd0c0fac9b 100644 --- a/apps/meteor/client/views/admin/customSounds/CustomSoundsTable/CustomSoundsTable.tsx +++ b/apps/meteor/client/views/admin/customSounds/CustomSoundsTable/CustomSoundsTable.tsx @@ -64,7 +64,7 @@ const CustomSoundsTable = ({ reload, onClick }: CustomSoundsTableProps) => { return ( <> - setParams(text)} /> + {isLoading && ( {headers} diff --git a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusTable/CustomUserStatusTable.tsx b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusTable/CustomUserStatusTable.tsx index 59c4cdd74eb33..d18fc4a706a8b 100644 --- a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusTable/CustomUserStatusTable.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusTable/CustomUserStatusTable.tsx @@ -70,7 +70,7 @@ const CustomUserStatus = ({ reload, onClick }: CustomUserStatusProps): ReactElem return ( <> - setText(text)} /> + {data.length === 0 && } {data && data.length > 0 && ( <> diff --git a/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx b/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx index 026f1cb0de30b..ebd1e762ff805 100644 --- a/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx +++ b/apps/meteor/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx @@ -76,7 +76,7 @@ const DeviceManagementAdminTable = ({ reloadRef }: { reloadRef: MutableRefObject return ( <> - setText(text)} /> + { return ( <> - setText(text)} /> + {isLoading && ( {headers} diff --git a/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx b/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx index 4fde3568b2aa3..5414e3d06aa54 100644 --- a/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx +++ b/apps/meteor/client/views/admin/moderation/helpers/ModerationFilter.tsx @@ -1,5 +1,5 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; -import React, { useCallback } from 'react'; +import React from 'react'; import FilterByText from '../../../../components/FilterByText'; import DateRangePicker from './DateRangePicker'; @@ -12,10 +12,8 @@ type ModerationFilterProps = { const ModerationFilter = ({ setText, setDateRange }: ModerationFilterProps) => { const t = useTranslation(); - const handleChange = useCallback(({ text }): void => setText(text), [setText]); - return ( - + ); diff --git a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx index 1ed21c1234a9a..d52d45415c8a7 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx @@ -9,7 +9,6 @@ const initialRoomTypeFilterStructure = [ { id: 'filter_by_room', text: 'Filter_by_room', - isGroupTitle: true, }, { id: 'd', @@ -71,7 +70,7 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch>; + ); return ( void; _id: string; i18nLabel: string; i18nDescription?: string; @@ -24,6 +25,7 @@ type GroupPageProps = { const GroupPage = ({ children = undefined, headerButtons = undefined, + onClickBack, _id, i18nLabel, i18nDescription = undefined, @@ -31,7 +33,6 @@ const GroupPage = ({ isCustom = false, }: GroupPageProps) => { const t = useTranslation(); - const router = useRoute('admin-settings'); const dispatch = useSettingsDispatch(); const dispatchToastMessage = useToastMessageDispatch(); @@ -56,7 +57,7 @@ const GroupPage = ({ const isColorSetting = (setting: ISetting): setting is ISettingColor => setting.type === 'color'; - const save = useMutableCallback(async () => { + const save = useEffectEvent(async () => { const changes = changedEditableSettings.map((setting) => { if (isColorSetting(setting)) { return { @@ -86,7 +87,7 @@ const GroupPage = ({ const dispatchToEditing = useEditableSettingsDispatch(); - const cancel = useMutableCallback(() => { + const cancel = useEffectEvent(() => { const settingsToDispatch = changedEditableSettings .map(({ _id }) => originalSettings.find((setting) => setting._id === _id)) .map((setting) => { @@ -118,8 +119,6 @@ const GroupPage = ({ save(); }; - const handleBack = useMutableCallback(() => router.push({})); - const handleCancelClick = (event: MouseEvent): void => { event.preventDefault(); cancel(); @@ -139,7 +138,7 @@ const GroupPage = ({ return ( - + {headerButtons} {tabs} diff --git a/apps/meteor/client/views/admin/settings/GroupSelector.tsx b/apps/meteor/client/views/admin/settings/GroupSelector.tsx index defe2a0a9a450..6d6d90a566eb7 100644 --- a/apps/meteor/client/views/admin/settings/GroupSelector.tsx +++ b/apps/meteor/client/views/admin/settings/GroupSelector.tsx @@ -11,9 +11,10 @@ import VoipGroupPage from './groups/VoipGroupPage'; type GroupSelectorProps = { groupId: GroupId; + onClickBack?: () => void; }; -const GroupSelector = ({ groupId }: GroupSelectorProps) => { +const GroupSelector = ({ groupId, onClickBack }: GroupSelectorProps) => { const group = useSettingStructure(groupId); if (!group) { @@ -21,22 +22,22 @@ const GroupSelector = ({ groupId }: GroupSelectorProps) => { } if (groupId === 'Assets') { - return ; + return ; } if (groupId === 'OAuth') { - return ; + return ; } if (groupId === 'LDAP') { - return ; + return ; } if (groupId === 'Call_Center') { - return ; + return ; } - return ; + return ; }; export default GroupSelector; diff --git a/apps/meteor/client/views/admin/settings/SettingsRoute.tsx b/apps/meteor/client/views/admin/settings/SettingsRoute.tsx index d0c0c00037787..c03aced8b5a04 100644 --- a/apps/meteor/client/views/admin/settings/SettingsRoute.tsx +++ b/apps/meteor/client/views/admin/settings/SettingsRoute.tsx @@ -1,4 +1,4 @@ -import { useRouteParameter, useIsPrivilegedSettingsContext } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useIsPrivilegedSettingsContext, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,6 +10,7 @@ import SettingsPage from './SettingsPage'; export const SettingsRoute = (): ReactElement => { const hasPermission = useIsPrivilegedSettingsContext(); const groupId = useRouteParameter('group'); + const router = useRouter(); if (!hasPermission) { return ; @@ -21,7 +22,7 @@ export const SettingsRoute = (): ReactElement => { return ( - + router.navigate('/admin/settings')} /> ); }; diff --git a/apps/meteor/client/views/admin/settings/groups/AssetsGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/AssetsGroupPage.tsx index 6d4bb90e3d128..a5935eb47bc8c 100644 --- a/apps/meteor/client/views/admin/settings/groups/AssetsGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/AssetsGroupPage.tsx @@ -6,14 +6,16 @@ import { useEditableSettingsGroupSections } from '../../EditableSettingsContext' import GroupPage from '../GroupPage'; import Section from '../Section'; -type AssetsGroupPageProps = ISetting; +type AssetsGroupPageProps = ISetting & { + onClickBack?: () => void; +}; -function AssetsGroupPage({ _id, ...group }: AssetsGroupPageProps): ReactElement { +function AssetsGroupPage({ _id, onClickBack, ...group }: AssetsGroupPageProps): ReactElement { const sections = useEditableSettingsGroupSections(_id); const solo = sections.length === 1; return ( - + {sections.map((sectionName) => (
))} diff --git a/apps/meteor/client/views/admin/settings/groups/GenericGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/GenericGroupPage.tsx index 6f71ec9c13332..c9148547b955a 100644 --- a/apps/meteor/client/views/admin/settings/groups/GenericGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/GenericGroupPage.tsx @@ -6,14 +6,16 @@ import { useEditableSettingsGroupSections } from '../../EditableSettingsContext' import GroupPage from '../GroupPage'; import Section from '../Section'; -type GenericGroupPageProps = ISetting; +type GenericGroupPageProps = ISetting & { + onClickBack?: () => void; +}; -function GenericGroupPage({ _id, ...props }: GenericGroupPageProps): ReactElement { +function GenericGroupPage({ _id, onClickBack, ...props }: GenericGroupPageProps): ReactElement { const sections = useEditableSettingsGroupSections(_id); const solo = sections.length === 1; return ( - + {sections.map((sectionName) => (
))} diff --git a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx index bea9ccd762220..a497738b9541d 100644 --- a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx @@ -10,7 +10,11 @@ import { useExternalLink } from '../../../../hooks/useExternalLink'; import { useEditableSettings } from '../../EditableSettingsContext'; import TabbedGroupPage from './TabbedGroupPage'; -function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element { +type LDAPGroupPageProps = ISetting & { + onClickBack?: () => void; +}; + +function LDAPGroupPage({ _id, onClickBack, ...group }: LDAPGroupPageProps) { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const testConnection = useEndpoint('POST', '/v1/ldap.testConnection'); @@ -127,6 +131,7 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element { return ( diff --git a/apps/meteor/client/views/admin/settings/groups/OAuthGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/OAuthGroupPage.tsx index 0bd65a3ad533f..713a269359944 100644 --- a/apps/meteor/client/views/admin/settings/groups/OAuthGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/OAuthGroupPage.tsx @@ -12,9 +12,11 @@ import GroupPage from '../GroupPage'; import Section from '../Section'; import CreateOAuthModal from './CreateOAuthModal'; -type OAuthGroupPageProps = ISetting; +type OAuthGroupPageProps = ISetting & { + onClickBack?: () => void; +}; -function OAuthGroupPage({ _id, ...group }: OAuthGroupPageProps): ReactElement { +function OAuthGroupPage({ _id, onClickBack, ...group }: OAuthGroupPageProps): ReactElement { const sections = useEditableSettingsGroupSections(_id); const solo = sections.length === 1; const t = useTranslation(); @@ -95,6 +97,7 @@ function OAuthGroupPage({ _id, ...group }: OAuthGroupPageProps): ReactElement { diff --git a/apps/meteor/client/views/admin/settings/groups/TabbedGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/TabbedGroupPage.tsx index f3546f13d758d..eeecf9cc38006 100644 --- a/apps/meteor/client/views/admin/settings/groups/TabbedGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/TabbedGroupPage.tsx @@ -12,9 +12,10 @@ import GenericGroupPage from './GenericGroupPage'; type TabbedGroupPageProps = ISetting & { headerButtons?: ReactElement; + onClickBack?: () => void; }; -function TabbedGroupPage({ _id, ...props }: TabbedGroupPageProps): JSX.Element { +function TabbedGroupPage({ _id, onClickBack, ...props }: TabbedGroupPageProps): JSX.Element { const t = useTranslation(); const tabs = useEditableSettingsGroupTabs(_id); @@ -25,7 +26,7 @@ function TabbedGroupPage({ _id, ...props }: TabbedGroupPageProps): JSX.Element { const solo = sections.length === 1; if (!tabs.length || (tabs.length === 1 && !tabs[0])) { - return ; + return ; } if (!tab && tabs[0]) { @@ -43,7 +44,7 @@ function TabbedGroupPage({ _id, ...props }: TabbedGroupPageProps): JSX.Element { ); return ( - + {sections.map((sectionName) => (
))} diff --git a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage.tsx index 82b55a152563a..3b7c873f22681 100644 --- a/apps/meteor/client/views/admin/settings/groups/VoipGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/VoipGroupPage.tsx @@ -11,7 +11,11 @@ import GroupPage from '../GroupPage'; import Section from '../Section'; import VoipExtensionsPage from './voip/VoipExtensionsPage'; -function VoipGroupPage({ _id, ...group }: ISetting): JSX.Element { +type VoipGroupPageProps = ISetting & { + onClickBack?: () => void; +}; + +function VoipGroupPage({ _id, onClickBack, ...group }: VoipGroupPageProps) { const t = useTranslation(); const voipEnabled = useSetting('VoIP_Enabled'); @@ -46,7 +50,7 @@ function VoipGroupPage({ _id, ...group }: ISetting): JSX.Element { ); return ( - + {tab === 'Extensions' ? ( ExtensionsPageComponent ) : ( diff --git a/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.stories.tsx b/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.stories.tsx index 5c8a468eed804..d235d716d4368 100644 --- a/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.stories.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.stories.tsx @@ -1,5 +1,6 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Field } from '@rocket.chat/fuselage'; -import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; diff --git a/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.tsx index 86c76c86f5029..2f94048e34067 100644 --- a/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/ActionSettingInput.tsx @@ -1,5 +1,6 @@ +import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Button, FieldRow, FieldHint } from '@rocket.chat/fuselage'; -import type { ServerMethods, TranslationKey } from '@rocket.chat/ui-contexts'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useMethod, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; diff --git a/apps/meteor/client/views/admin/settings/inputs/CodeMirror/CodeMirrorBox.tsx b/apps/meteor/client/views/admin/settings/inputs/CodeMirror/CodeMirrorBox.tsx index 3f61417e4fa68..fd96d715a837b 100644 --- a/apps/meteor/client/views/admin/settings/inputs/CodeMirror/CodeMirrorBox.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/CodeMirror/CodeMirrorBox.tsx @@ -1,51 +1,55 @@ -import { css } from '@rocket.chat/css-in-js'; import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useToggle } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, ReactNode } from 'react'; import React from 'react'; +import { createPortal } from 'react-dom'; const CodeMirrorBox = ({ label, children }: { label: ReactNode; children: ReactElement }) => { const t = useTranslation(); const [fullScreen, toggleFullScreen] = useToggle(false); - const fullScreenStyle = css` - position: fixed; - z-index: 100; - top: 0; - right: 0; - bottom: 0; - left: 0; - - display: flex; - - flex-direction: column; - - width: auto; - height: auto; - - padding: 40px; - - align-items: stretch; - `; - - return ( - - {fullScreen && ( + if (fullScreen) { + return createPortal( + {label} - )} + + {children} + + + + + + + , + document.getElementById('main-content') as HTMLElement, + ); + } + + return ( + {children} diff --git a/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx b/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx index 63fe2691d9720..e595acc469511 100644 --- a/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserFormWithData.tsx @@ -1,6 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { isUserFederated } from '@rocket.chat/core-typings'; import { Box, Callout } from '@rocket.chat/fuselage'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -16,7 +17,12 @@ type AdminUserFormWithDataProps = { const AdminUserFormWithData = ({ uid, onReload }: AdminUserFormWithDataProps): ReactElement => { const t = useTranslation(); - const { data, isLoading, isError } = useUserInfoQuery({ userId: uid }); + const { data, isLoading, isError, refetch } = useUserInfoQuery({ userId: uid }); + + const handleReload = useEffectEvent(() => { + onReload(); + refetch(); + }); if (isLoading) { return ( @@ -42,7 +48,7 @@ const AdminUserFormWithData = ({ uid, onReload }: AdminUserFormWithDataProps): R ); } - return ; + return ; }; export default AdminUserFormWithData; diff --git a/apps/meteor/client/views/admin/users/AdminUserInfoActions.tsx b/apps/meteor/client/views/admin/users/AdminUserInfoActions.tsx index c1052889f16f8..8002222820545 100644 --- a/apps/meteor/client/views/admin/users/AdminUserInfoActions.tsx +++ b/apps/meteor/client/views/admin/users/AdminUserInfoActions.tsx @@ -6,6 +6,7 @@ import React, { useCallback, useMemo } from 'react'; import { UserInfoAction } from '../../../components/UserInfo'; import { useActionSpread } from '../../hooks/useActionSpread'; +import type { AdminUserTab } from './AdminUsersPage'; import { useChangeAdminStatusAction } from './hooks/useChangeAdminStatusAction'; import { useChangeUserStatusAction } from './hooks/useChangeUserStatusAction'; import { useDeleteUserAction } from './hooks/useDeleteUserAction'; @@ -18,6 +19,7 @@ type AdminUserInfoActionsProps = { isFederatedUser: IUser['federated']; isActive: boolean; isAdmin: boolean; + tab: AdminUserTab; onChange: () => void; onReload: () => void; }; @@ -29,6 +31,7 @@ const AdminUserInfoActions = ({ isFederatedUser, isActive, isAdmin, + tab, onChange, onReload, }: AdminUserInfoActionsProps): ReactElement => { @@ -62,6 +65,7 @@ const AdminUserInfoActions = ({ [userId, userRoute], ); + const isNotPendingDeactivatedNorFederated = tab !== 'pending' && tab !== 'deactivated' && !isFederatedUser; const options = useMemo( () => ({ ...(canDirectMessage && { @@ -81,24 +85,25 @@ const AdminUserInfoActions = ({ disabled: isFederatedUser, }, }), - ...(changeAdminStatusAction && !isFederatedUser && { makeAdmin: changeAdminStatusAction }), - ...(resetE2EKeyAction && !isFederatedUser && { resetE2EKey: resetE2EKeyAction }), - ...(resetTOTPAction && !isFederatedUser && { resetTOTP: resetTOTPAction }), - ...(deleteUserAction && { delete: deleteUserAction }), + ...(isNotPendingDeactivatedNorFederated && changeAdminStatusAction && { makeAdmin: changeAdminStatusAction }), + ...(isNotPendingDeactivatedNorFederated && resetE2EKeyAction && { resetE2EKey: resetE2EKeyAction }), + ...(isNotPendingDeactivatedNorFederated && resetTOTPAction && { resetTOTP: resetTOTPAction }), ...(changeUserStatusAction && !isFederatedUser && { changeActiveStatus: changeUserStatusAction }), + ...(deleteUserAction && { delete: deleteUserAction }), }), [ - t, canDirectMessage, - directMessageClick, canEditOtherUserInfo, - editUserClick, changeAdminStatusAction, changeUserStatusAction, deleteUserAction, + directMessageClick, + editUserClick, + isFederatedUser, + isNotPendingDeactivatedNorFederated, resetE2EKeyAction, resetTOTPAction, - isFederatedUser, + t, ], ); @@ -117,7 +122,9 @@ const AdminUserInfoActions = ({ secondary flexShrink={0} key='menu' - renderItem={({ label: { label, icon }, ...props }): ReactElement =>
+ +## 0.6.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.1 +
+ +## 0.6.0-rc.0 + +### Minor Changes + +- ([#32493](https://github.com/RocketChat/Rocket.Chat/pull/32493)) Fixed Livechat rooms being displayed in the Engagement Dashboard's "Channels" tab + +- ([#32752](https://github.com/RocketChat/Rocket.Chat/pull/32752)) Added system messages support for Omnichannel PDF transcripts and email transcripts. Currently these transcripts don't render system messages and is shown as an empty message in PDF/email. This PR adds this support for all valid livechat system messages. + + Also added a new setting under transcripts, to toggle the inclusion of system messages in email and PDF transcripts. + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. +- ([#32493](https://github.com/RocketChat/Rocket.Chat/pull/32493)) Improved Engagement Dashboard's "Channels" tab performance by not returning rooms that had no activity in the analyzed period + +- ([#32800](https://github.com/RocketChat/Rocket.Chat/pull/32800)) Added the ability to filter chats by `queued` on the Current Chats Omnichannel page + +### Patch Changes + +-
Updated dependencies [2d89a0c448, 24f7df4894, b8e5887fb9]: + + - @rocket.chat/core-typings@6.11.0-rc.0 +
+ +## 0.5.2 + +### Patch Changes + +-
Updated dependencies [ca6a9d8de8, ca6a9d8de8, ca6a9d8de8, ca6a9d8de8]: + + - @rocket.chat/core-typings@6.10.2 +
+ +## 0.5.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.10.1 +
+ ## 0.5.0 ### Minor Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 372bde3257cbe..24e80c1f53564 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,12 +1,12 @@ { "name": "@rocket.chat/model-typings", - "version": "0.5.0", + "version": "0.6.0-rc.2", "private": true, "devDependencies": { - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "@types/node-rsa": "^1.1.3", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "mongodb": "^4.17.2", "ts-jest": "~29.1.1", "typescript": "~5.3.3" diff --git a/packages/model-typings/src/index.ts b/packages/model-typings/src/index.ts index 23e77ff1de29c..61ad5d1f5c550 100644 --- a/packages/model-typings/src/index.ts +++ b/packages/model-typings/src/index.ts @@ -79,3 +79,4 @@ export * from './models/IAuditLogModel'; export * from './models/ICronHistoryModel'; export * from './models/IMigrationsModel'; export * from './models/IModerationReportsModel'; +export * from './updater'; diff --git a/packages/model-typings/src/models/IAnalyticsModel.ts b/packages/model-typings/src/models/IAnalyticsModel.ts index 4d31f66a94d84..1ca4d7abf1186 100644 --- a/packages/model-typings/src/models/IAnalyticsModel.ts +++ b/packages/model-typings/src/models/IAnalyticsModel.ts @@ -2,6 +2,7 @@ import type { IAnalytic, IRoom } from '@rocket.chat/core-typings'; import type { AggregationCursor, FindCursor, FindOptions, UpdateResult, Document } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; +import type { IChannelsWithNumberOfMessagesBetweenDate } from './IRoomsModel'; export interface IAnalyticsModel extends IBaseModel { saveMessageSent({ room, date }: { room: IRoom; date: IAnalytic['date'] }): Promise; @@ -38,4 +39,12 @@ export interface IAnalyticsModel extends IBaseModel { users: number; }>; findByTypeBeforeDate({ type, date }: { type: IAnalytic['type']; date: IAnalytic['date'] }): FindCursor; + findRoomsByTypesWithNumberOfMessagesBetweenDate(params: { + types: Array; + start: number; + end: number; + startOfLastWeek: number; + endOfLastWeek: number; + options?: any; + }): AggregationCursor<{ channels: IChannelsWithNumberOfMessagesBetweenDate[]; total: number }>; } diff --git a/packages/model-typings/src/models/ILivechatRoomsModel.ts b/packages/model-typings/src/models/ILivechatRoomsModel.ts index 6384b325f991e..05c00e44951f5 100644 --- a/packages/model-typings/src/models/ILivechatRoomsModel.ts +++ b/packages/model-typings/src/models/ILivechatRoomsModel.ts @@ -9,6 +9,7 @@ import type { import type { FindCursor, UpdateResult, AggregationCursor, Document, FindOptions, DeleteResult, Filter } from 'mongodb'; import type { FindPaginated } from '..'; +import type { Updater } from '../updater'; import type { IBaseModel } from './IBaseModel'; type Period = { @@ -28,7 +29,10 @@ type WithOptions = { options?: any; }; +// TODO: Fix types of model export interface ILivechatRoomsModel extends IBaseModel { + getUpdater(): Updater; + getQueueMetrics(params: { departmentId: any; agentId: any; includeOfflineAgents: any; options?: any }): any; findAllNumberOfAbandonedRooms(params: Period & WithDepartment & WithOnlyCount & WithOptions): Promise; @@ -96,6 +100,7 @@ export interface ILivechatRoomsModel extends IBaseModel { visitorId?: any; roomIds?: any; onhold: any; + queued: any; options?: any; extraQuery?: any; }): FindPaginated>; @@ -203,14 +208,18 @@ export interface ILivechatRoomsModel extends IBaseModel { options?: FindOptions, extraQuery?: Filter, ): FindCursor; - setResponseByRoomId(roomId: string, responseBy: IOmnichannelRoom['responseBy']): Promise; + getResponseByRoomIdUpdateQuery( + responseBy: IOmnichannelRoom['responseBy'], + updater?: Updater, + ): Updater; setNotResponseByRoomId(roomId: string): Promise; - setAgentLastMessageTs(roomId: string): Promise; - saveAnalyticsDataByRoomId( + getAgentLastMessageTsUpdateQuery(updater?: Updater): Updater; + getAnalyticsUpdateQueryByRoomId( room: IOmnichannelRoom, message: IMessage, - analyticsData?: Record, - ): Promise; + analyticsData: Record | undefined, + updater?: Updater, + ): Promise>; getTotalConversationsBetweenDate(t: 'l', date: { gte: Date; lt: Date }, data?: { departmentId: string }): Promise; getAnalyticsMetricsBetweenDate( t: 'l', @@ -238,7 +247,7 @@ export interface ILivechatRoomsModel extends IBaseModel { removeAgentByRoomId(roomId: string): Promise; removeByVisitorToken(token: string): Promise; removeById(_id: string): Promise; - setVisitorLastMessageTimestampByRoomId(roomId: string, lastMessageTs: Date): Promise; + getVisitorLastMessageTsUpdateQueryByRoomId(lastMessageTs: Date, updater?: Updater): Updater; setVisitorInactivityInSecondsById(roomId: string, visitorInactivity: any): Promise; changeVisitorByRoomId(roomId: string, visitor: { _id: string; username: string; token: string }): Promise; unarchiveOneById(roomId: string): Promise; diff --git a/packages/model-typings/src/models/IMessagesModel.ts b/packages/model-typings/src/models/IMessagesModel.ts index 0fb02a778c9dd..986a50c8cd299 100644 --- a/packages/model-typings/src/models/IMessagesModel.ts +++ b/packages/model-typings/src/models/IMessagesModel.ts @@ -71,7 +71,12 @@ export interface IMessagesModel extends IBaseModel { findLivechatClosedMessages(rid: IRoom['_id'], searchTerm?: string, options?: FindOptions): FindPaginated>; findLivechatMessages(rid: IRoom['_id'], options?: FindOptions): FindCursor; - findLivechatMessagesWithoutClosing(rid: IRoom['_id'], options?: FindOptions): FindCursor; + findLivechatMessagesWithoutTypes( + rid: IRoom['_id'], + ignoredTypes: IMessage['t'][], + showSystemMessages: boolean, + options?: FindOptions, + ): FindCursor; countRoomsWithStarredMessages(options: AggregateOptions): Promise; countRoomsWithPinnedMessages(options: AggregateOptions): Promise; @@ -113,6 +118,7 @@ export interface IMessagesModel extends IBaseModel { roomId: IRoom['_id'], types: IMessage['t'][], ts: Date, + showSystemMessages: boolean, options?: FindOptions, showThreadMessages?: boolean, ): FindCursor; diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index 887f51987ae68..a88a5bde7f4ea 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -86,7 +86,8 @@ export interface IRoomsModel extends IBaseModel { setTeamDefaultById(rid: IRoom['_id'], teamDefault: NonNullable, options?: UpdateOptions): Promise; - findChannelsWithNumberOfMessagesBetweenDate(params: { + findChannelsByTypesWithNumberOfMessagesBetweenDate(params: { + types: Array; start: number; end: number; startOfLastWeek: number; @@ -94,14 +95,6 @@ export interface IRoomsModel extends IBaseModel { options?: any; }): AggregationCursor; - countChannelsWithNumberOfMessagesBetweenDate(params: { - start: number; - end: number; - startOfLastWeek: number; - endOfLastWeek: number; - options?: any; - }): AggregationCursor<{ total: number }>; - findOneByName(name: NonNullable, options?: FindOptions): Promise; findDefaultRoomsForTeam(teamId: any): FindCursor; @@ -126,6 +119,8 @@ export interface IRoomsModel extends IBaseModel { setRoomNameById(roomId: IRoom['_id'], name: IRoom['name']): Promise; + setSidepanelById(roomId: IRoom['_id'], sidepanel: IRoom['sidepanel']): Promise; + setFnameById(_id: IRoom['_id'], fname: IRoom['fname']): Promise; setRoomTopicById(roomId: IRoom['_id'], topic: IRoom['description']): Promise; diff --git a/packages/model-typings/src/models/IVideoConferenceModel.ts b/packages/model-typings/src/models/IVideoConferenceModel.ts index 5653859f04f6f..8ef775fb60822 100644 --- a/packages/model-typings/src/models/IVideoConferenceModel.ts +++ b/packages/model-typings/src/models/IVideoConferenceModel.ts @@ -61,4 +61,10 @@ export interface IVideoConferenceModel extends IBaseModel { updateUserReferences(userId: IUser['_id'], username: IUser['username'], name: IUser['name']): Promise; increaseAnonymousCount(callId: IGroupVideoConference['_id']): Promise; + + unsetDiscussionRidById(callId: string): Promise; + + setDiscussionRidById(callId: string, discussionRid: IRoom['_id']): Promise; + + unsetDiscussionRid(discussionRid: IRoom['_id']): Promise; } diff --git a/packages/model-typings/src/updater.ts b/packages/model-typings/src/updater.ts new file mode 100644 index 0000000000000..33ec78f4b86be --- /dev/null +++ b/packages/model-typings/src/updater.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type { Join, NestedPaths, PropertyType, ArrayElement, NestedPathsOfType, Filter } from 'mongodb'; + +export interface Updater { + set

, K extends keyof P>(key: K, value: P[K]): Updater; + unset>(key: K): Updater; + inc>(key: K, value: number): Updater; + addToSet>(key: K, value: AddToSetProps[K]): Updater; + persist(query: Filter): Promise; + hasChanges(): boolean; +} + +export type SetProps = Readonly< + { + [Property in Join, '.'>]: PropertyType; + } & { + [Property in `${NestedPathsOfType}.$${`[${string}]` | ''}`]: ArrayElement< + PropertyType + >; + } & { + [Property in `${NestedPathsOfType[]>}.$${`[${string}]` | ''}.${string}`]: any; + } +>; + +type GetType = { + [Key in keyof T]: K extends T[Key] ? T[Key] : never; +}; + +type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] }; + +// only allow optional properties +export type UnsetProps = OmitNever, undefined>>; + +export type IncProps = OmitNever, number>>; + +export type AddToSetProps = OmitNever, any[]>>; diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index 4b30b3f974ce3..375dd26f6aeb4 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,5 +1,51 @@ # @rocket.chat/models +## 0.2.0-rc.2 + +### Patch Changes + +-

Updated dependencies []: + + - @rocket.chat/model-typings@0.6.0-rc.2 +
+ +## 0.2.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + - @rocket.chat/model-typings@0.6.0-rc.1 +
+ +## 0.2.0-rc.0 + +### Minor Changes + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. + +### Patch Changes + +-
Updated dependencies [439faa87d3, 03c8b066f9, 2d89a0c448, 439faa87d3, 264d7d5496]: + + - @rocket.chat/model-typings@0.6.0-rc.0 +
+ +### Patch Changes + +-
Updated dependencies []: + - @rocket.chat/model-typings@0.5.2 +
+ +## 0.1.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/model-typings@0.5.1 +
+ ## 0.1.0 ### Minor Changes diff --git a/packages/models/package.json b/packages/models/package.json index c5e9cfff578e4..7ff6b9ee2b8f9 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,13 +1,13 @@ { "name": "@rocket.chat/models", - "version": "0.1.0", + "version": "0.2.0-rc.2", "private": true, "devDependencies": { "@swc/core": "^1.3.95", "@swc/jest": "^0.2.29", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "^29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index e1cf91f1b0ee7..655a94923febe 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -88,6 +88,7 @@ export function getCollectionName(name: string): string { } export { registerModel } from './proxify'; +export { type Updater, UpdaterImpl } from './updater'; export const Apps = proxify('IAppsModel'); export const AppsTokens = proxify('IAppsTokensModel'); diff --git a/packages/models/src/updater.spec.ts b/packages/models/src/updater.spec.ts new file mode 100644 index 0000000000000..4decc57d7c3ba --- /dev/null +++ b/packages/models/src/updater.spec.ts @@ -0,0 +1,192 @@ +import { UpdaterImpl } from './updater'; + +test('updater typings', () => { + const updater = new UpdaterImpl<{ + _id: string; + t: 'l'; + a: { + b: string; + }; + c?: number; + + d?: { + e: string; + }; + e: string[]; + }>({} as any); + + updater.addToSet('e', 'a'); + + // @ts-expect-error + updater.addToSet('e', 1); + // @ts-expect-error + updater.addToSet('a', 'b'); + + // @ts-expect-error + updater.set('njame', 1); + // @ts-expect-error + updater.set('ttes', 1); + // @ts-expect-error + updater.set('t', 'a'); + updater.set('t', 'l'); + // @ts-expect-error + updater.set('a', 'b'); + // @ts-expect-error + updater.set('c', 'b'); + updater.set('c', 1); + + updater.set('a', { + b: 'set', + }); + updater.set('a.b', 'test'); + + // @ts-expect-error + updater.unset('a'); + + updater.unset('c'); + + updater.unset('d'); + + updater.unset('d.e'); + // @ts-expect-error + updater.inc('d', 1); + updater.inc('c', 1); +}); + +test('updater $set operations', async () => { + const updateOne = jest.fn(); + + const updater = new UpdaterImpl<{ + _id: string; + t: 'l'; + a: { + b: string; + }; + c?: number; + }>({ + updateOne, + } as any); + + updater.set('a', { + b: 'set', + }); + + await updater.persist({ + _id: 'test', + }); + + expect(updateOne).toBeCalledWith( + { + _id: 'test', + }, + { $set: { a: { b: 'set' } } }, + ); +}); + +test('updater $unset operations', async () => { + const updateOne = jest.fn(); + + const updater = new UpdaterImpl<{ + _id: string; + t: 'l'; + a: { + b: string; + }; + c?: number; + }>({ + updateOne, + } as any); + + updater.unset('c'); + + await updater.persist({ + _id: 'test', + }); + + expect(updateOne).toBeCalledWith( + { + _id: 'test', + }, + { $unset: { c: 1 } }, + ); +}); + +test('updater inc multiple operations', async () => { + const updateOne = jest.fn(); + + const updater = new UpdaterImpl<{ + _id: string; + t: 'l'; + a: { + b: string; + }; + c?: number; + }>({ + updateOne, + } as any); + + updater.inc('c', 1); + updater.inc('c', 1); + + await updater.persist({ + _id: 'test', + }); + + expect(updateOne).toBeCalledWith( + { + _id: 'test', + }, + { $inc: { c: 2 } }, + ); +}); + +test('it should add items to array', async () => { + const updateOne = jest.fn(); + const updater = new UpdaterImpl<{ + _id: string; + a: string[]; + }>({ + updateOne, + } as any); + + updater.addToSet('a', 'b'); + updater.addToSet('a', 'c'); + + await updater.persist({ + _id: 'test', + }); + + expect(updateOne).toBeCalledWith( + { + _id: 'test', + }, + { $addToSet: { $each: { a: ['b', 'c'] } } }, + ); +}); + +test('it should persist only once', async () => { + const updateOne = jest.fn(); + + const updater = new UpdaterImpl<{ + _id: string; + t: 'l'; + a: { + b: string; + }; + c?: number; + }>({ + updateOne, + } as any); + + updater.set('a', { + b: 'set', + }); + + await updater.persist({ + _id: 'test', + }); + + expect(updateOne).toBeCalledTimes(1); + + expect(() => updater.persist({ _id: 'test' })).rejects.toThrow(); +}); diff --git a/packages/models/src/updater.ts b/packages/models/src/updater.ts new file mode 100644 index 0000000000000..ef163ea09e40e --- /dev/null +++ b/packages/models/src/updater.ts @@ -0,0 +1,84 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type { IBaseModel, Updater, SetProps, UnsetProps, IncProps, AddToSetProps } from '@rocket.chat/model-typings'; +import type { UpdateFilter, Filter } from 'mongodb'; + +type ArrayElementType = T extends (infer E)[] ? E : T; + +type Keys = keyof SetProps; + +export class UpdaterImpl implements Updater { + private _set: Map, any> | undefined; + + private _unset: Set> | undefined; + + private _inc: Map, number> | undefined; + + private _addToSet: Map, any[]> | undefined; + + private dirty = false; + + constructor(private model: IBaseModel) {} + + set

, K extends keyof P>(key: K, value: P[K]) { + this._set = this._set ?? new Map, any>(); + this._set.set(key as Keys, value); + return this; + } + + unset>(key: K): Updater { + this._unset = this._unset ?? new Set>(); + this._unset.add(key); + return this; + } + + inc>(key: K, value: number): Updater { + this._inc = this._inc ?? new Map, number>(); + + const prev = this._inc.get(key) ?? 0; + this._inc.set(key, prev + value); + return this; + } + + addToSet>(key: K, value: ArrayElementType[K]>): Updater { + this._addToSet = this._addToSet ?? new Map, any[]>(); + + const prev = this._addToSet.get(key) ?? []; + this._addToSet.set(key, [...prev, value]); + return this; + } + + async persist(query: Filter): Promise { + if (this.dirty) { + throw new Error('Updater is not dirty'); + } + + if ((process.env.NODE_ENV === 'development' || process.env.TEST_MODE) && !this.hasChanges()) { + throw new Error('Nothing to update'); + } + + this.dirty = true; + + const update = this.getUpdateFilter(); + try { + await this.model.updateOne(query, update); + } catch (error) { + console.error('Failed to update', JSON.stringify(query), JSON.stringify(update, null, 2)); + throw error; + } + } + + hasChanges() { + return Object.keys(this.getUpdateFilter()).length > 0; + } + + private getUpdateFilter() { + return { + ...(this._set && { $set: Object.fromEntries(this._set) }), + ...(this._unset && { $unset: Object.fromEntries([...this._unset.values()].map((k) => [k, 1])) }), + ...(this._inc && { $inc: Object.fromEntries(this._inc) }), + ...(this._addToSet && { $addToSet: { $each: Object.fromEntries(this._addToSet) } }), + } as unknown as UpdateFilter; + } +} + +export { Updater }; diff --git a/packages/node-poplib/package.json b/packages/node-poplib/package.json index 9e9a112945e6d..9fa4a74db1e15 100644 --- a/packages/node-poplib/package.json +++ b/packages/node-poplib/package.json @@ -3,9 +3,9 @@ "version": "0.0.2", "private": true, "devDependencies": { - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/password-policies/package.json b/packages/password-policies/package.json index 4b2f0c76b7ae5..d243fe0796306 100644 --- a/packages/password-policies/package.json +++ b/packages/password-policies/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/chai": "~4.3.16", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "chai": "^4.3.10", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/patch-injection/package.json b/packages/patch-injection/package.json index e90f662ac202f..d2417cb04df48 100644 --- a/packages/patch-injection/package.json +++ b/packages/patch-injection/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/random/package.json b/packages/random/package.json index 343ce2344ab18..f1a0ac5de8f74 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -20,8 +20,8 @@ "@typescript-eslint/eslint-plugin": "~5.60.1", "@typescript-eslint/parser": "~5.60.1", "eslint": "~8.45.0", - "jest": "~29.6.4", - "jest-environment-jsdom": "~29.6.4", + "jest": "~29.7.0", + "jest-environment-jsdom": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 00affd2f294b0..3be5f4643578f 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,5 +1,65 @@ # @rocket.chat/rest-typings +## 6.11.0-rc.2 + +### Patch Changes + +-

Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.2 +
+ +## 6.11.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.1 +
+ +## 6.11.0-rc.0 + +### Minor Changes + +- ([#32800](https://github.com/RocketChat/Rocket.Chat/pull/32800)) Added the ability to filter chats by `queued` on the Current Chats Omnichannel page + +### Patch Changes + +- ([#32719](https://github.com/RocketChat/Rocket.Chat/pull/32719)) Added the `user` param to apps-engine update method call, allowing apps' new `onUpdate` hook to know who triggered the update. + +-
Updated dependencies [2d89a0c448, 24f7df4894, b8e5887fb9]: + + - @rocket.chat/core-typings@6.11.0-rc.0 + - @rocket.chat/ui-kit@0.36.0-rc.0 +
+ +## 6.10.2 + +### Patch Changes + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that prevented apps from being updated or uninstalled in some cases + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that prevented apps from handling errors during execution in some cases + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Improved Apps-Engine installation to prevent start up errors on manual installation setups + +- ([#32935](https://github.com/RocketChat/Rocket.Chat/pull/32935)) Fixed an issue that caused the video conference button on rooms to not recognize a video conference provider app in some cases + +-
Updated dependencies [ca6a9d8de8, ca6a9d8de8, ca6a9d8de8, ca6a9d8de8]: + + - @rocket.chat/core-typings@6.10.2 +
+ +## 6.10.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.10.1 +
+ ## 6.10.0 ### Minor Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 9be27e7634cbc..cd0ada27810a6 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,12 +1,12 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.11.0-develop", + "version": "6.12.0-develop", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", - "jest-environment-jsdom": "~29.6.4", + "jest": "~29.7.0", + "jest-environment-jsdom": "~29.7.0", "mongodb": "^4.17.2", "ts-jest": "~29.1.1", "typescript": "~5.3.3" diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 3dad53cbe8d29..ec53304605fc6 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -2552,6 +2552,7 @@ export type GETLivechatRoomsParams = PaginatedRequest<{ departmentId?: string; open?: string | boolean; onhold?: string | boolean; + queued?: string | boolean; tags?: string[]; }>; @@ -2617,6 +2618,12 @@ const GETLivechatRoomsParamsSchema = { { type: 'boolean', nullable: true }, ], }, + queued: { + anyOf: [ + { type: 'string', nullable: true }, + { type: 'boolean', nullable: true }, + ], + }, tags: { type: 'array', items: { diff --git a/packages/rest-typings/src/v1/teams/index.ts b/packages/rest-typings/src/v1/teams/index.ts index 0a1583b66474a..d63e6da8bd8a9 100644 --- a/packages/rest-typings/src/v1/teams/index.ts +++ b/packages/rest-typings/src/v1/teams/index.ts @@ -89,6 +89,7 @@ export type TeamsEndpoints = { }; }; owner?: IUser['_id']; + sidepanel?: IRoom['sidepanel']; }) => { team: ITeam; }; diff --git a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts index 1c89fdc04d5dc..7ef41d363dea0 100644 --- a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts @@ -1,4 +1,4 @@ -import type { ThemePreference } from '@rocket.chat/ui-theming/src/types/themes'; +import type { ThemePreference } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; const ajv = new Ajv({ diff --git a/packages/server-cloud-communication/package.json b/packages/server-cloud-communication/package.json index 878a0565306b6..15310d3430d16 100644 --- a/packages/server-cloud-communication/package.json +++ b/packages/server-cloud-communication/package.json @@ -4,9 +4,9 @@ "private": true, "devDependencies": { "@rocket.chat/license": "workspace:^", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/server-fetch/package.json b/packages/server-fetch/package.json index e1527e9287038..cb96395099ac4 100644 --- a/packages/server-fetch/package.json +++ b/packages/server-fetch/package.json @@ -3,9 +3,9 @@ "version": "0.0.3", "private": true, "devDependencies": { - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/sha256/package.json b/packages/sha256/package.json index 1b2f1fa6b5230..b56d25ad3713a 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -19,7 +19,7 @@ "@typescript-eslint/eslint-plugin": "~5.60.1", "@typescript-eslint/parser": "~5.60.1", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 86ecb2a3c6ec4..bda8e7510825c 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/tools +## 0.2.2-rc.0 + +### Patch Changes + +- ([#32527](https://github.com/RocketChat/Rocket.Chat/pull/32527)) Fixed an inconsistent evaluation of the `Accounts_LoginExpiration` setting over the codebase. In some places, it was being used as milliseconds while in others as days. Invalid values produced different results. A helper function was created to centralize the setting validation and the proper value being returned to avoid edge cases. + Negative values may be saved on the settings UI panel but the code will interpret any negative, NaN or 0 value to the default expiration which is 90 days. + ## 0.2.1 ### Patch Changes diff --git a/packages/tools/jest.config.ts b/packages/tools/jest.config.ts new file mode 100644 index 0000000000000..959a31a7c6bfc --- /dev/null +++ b/packages/tools/jest.config.ts @@ -0,0 +1,3 @@ +export default { + preset: 'ts-jest', +}; diff --git a/packages/tools/package.json b/packages/tools/package.json index e6bbae9b5be6e..f100965719811 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,11 +1,11 @@ { "name": "@rocket.chat/tools", - "version": "0.2.1", + "version": "0.2.2-rc.0", "private": true, "devDependencies": { - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, @@ -13,7 +13,9 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx .", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "test": "jest", + "test:cov": "jest --coverage", "build": "rm -rf dist && tsc -p tsconfig.json", + "testunit": "jest", "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" }, "main": "./dist/index.js", diff --git a/packages/tools/src/converter.spec.ts b/packages/tools/src/converter.spec.ts new file mode 100644 index 0000000000000..5a27b6a97b95a --- /dev/null +++ b/packages/tools/src/converter.spec.ts @@ -0,0 +1,15 @@ +import { convertFromDaysToMilliseconds } from './converter'; + +describe('convertFromDaysToMilliseconds', () => { + it('should throw an error when a non number is passed', () => { + // @ts-expect-error - Testing + expect(() => convertFromDaysToMilliseconds('90')).toThrow(); + }); + it('should return the value passed when its valid', () => { + expect(convertFromDaysToMilliseconds(85)).toBe(85 * 24 * 60 * 60 * 1000); + }); + it('should fail if anything but an integer is passed', () => { + expect(() => convertFromDaysToMilliseconds(85.5)).toThrow(); + expect(() => convertFromDaysToMilliseconds(-2.3)).toThrow(); + }); +}); diff --git a/packages/tools/src/converter.ts b/packages/tools/src/converter.ts new file mode 100644 index 0000000000000..e71c264857dcb --- /dev/null +++ b/packages/tools/src/converter.ts @@ -0,0 +1,7 @@ +export const convertFromDaysToMilliseconds = (days: number) => { + if (typeof days !== 'number' || !Number.isInteger(days)) { + throw new Error('days must be a number'); + } + + return days * 24 * 60 * 60 * 1000; +}; diff --git a/packages/tools/src/getLoginExpiration.spec.ts b/packages/tools/src/getLoginExpiration.spec.ts new file mode 100644 index 0000000000000..edd652172a5e4 --- /dev/null +++ b/packages/tools/src/getLoginExpiration.spec.ts @@ -0,0 +1,35 @@ +import { getLoginExpirationInDays, getLoginExpirationInMs } from './getLoginExpiration'; + +describe('getLoginExpirationInDays', () => { + it('should return 90 by default', () => { + expect(getLoginExpirationInDays()).toBe(90); + }); + it('should return 90 when value is 0', () => { + expect(getLoginExpirationInDays(0)).toBe(90); + }); + it('should return 90 when value is NaN', () => { + expect(getLoginExpirationInDays(NaN)).toBe(90); + }); + it('should return 90 when value is negative', () => { + expect(getLoginExpirationInDays(-1)).toBe(90); + }); + it('should return 90 when value is undefined', () => { + expect(getLoginExpirationInDays(undefined)).toBe(90); + }); + it('should return 90 when value is not a number', () => { + // @ts-expect-error - Testing + expect(getLoginExpirationInDays('90')).toBe(90); + }); + it('should return the value passed when its valid', () => { + expect(getLoginExpirationInDays(85)).toBe(85); + }); +}); + +describe('getLoginExpirationInMs', () => { + it('should return 90 days in milliseconds when no value is passed', () => { + expect(getLoginExpirationInMs()).toBe(90 * 24 * 60 * 60 * 1000); + }); + it('should return the value passed when its valid', () => { + expect(getLoginExpirationInMs(85)).toBe(85 * 24 * 60 * 60 * 1000); + }); +}); diff --git a/packages/tools/src/getLoginExpiration.ts b/packages/tools/src/getLoginExpiration.ts new file mode 100644 index 0000000000000..de21fdce21a87 --- /dev/null +++ b/packages/tools/src/getLoginExpiration.ts @@ -0,0 +1,16 @@ +import { convertFromDaysToMilliseconds } from './converter'; + +const ACCOUNTS_DEFAULT_LOGIN_EXPIRATION_DAYS = 90; + +// Given a value, validates if it mets the conditions to be a valid login expiration. +// Else, returns the default login expiration (which for Meteor is 90 days) +export const getLoginExpirationInDays = (expiry?: number) => { + if (expiry && typeof expiry === 'number' && !Number.isNaN(expiry) && expiry > 0) { + return expiry; + } + return ACCOUNTS_DEFAULT_LOGIN_EXPIRATION_DAYS; +}; + +export const getLoginExpirationInMs = (expiry?: number) => { + return convertFromDaysToMilliseconds(getLoginExpirationInDays(expiry)); +}; diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index b1b53ab71a90a..96faa4d55969e 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -4,3 +4,5 @@ export * from './pick'; export * from './stream'; export * from './timezone'; export * from './wrapExceptions'; +export * from './getLoginExpiration'; +export * from './converter'; diff --git a/packages/ui-avatar/CHANGELOG.md b/packages/ui-avatar/CHANGELOG.md index f45e6241438aa..f0b0b7b7fe99a 100644 --- a/packages/ui-avatar/CHANGELOG.md +++ b/packages/ui-avatar/CHANGELOG.md @@ -1,5 +1,50 @@ # @rocket.chat/ui-avatar +## 5.0.0-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.2 +
+ +## 5.0.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.1 +
+ +## 5.0.0-rc.0 + +### Patch Changes + +-
Updated dependencies [2d89a0c448, 4e8aa575a6]: + + - @rocket.chat/ui-contexts@9.0.0-rc.0 +
+ +## 4.0.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.2 +
+ +## 4.0.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.1 +
+ ## 4.0.0 ### Minor Changes diff --git a/packages/ui-avatar/package.json b/packages/ui-avatar/package.json index 2e45c20f1d36a..71a7d0e8bbf44 100644 --- a/packages/ui-avatar/package.json +++ b/packages/ui-avatar/package.json @@ -1,10 +1,10 @@ { "name": "@rocket.chat/ui-avatar", - "version": "4.0.0", + "version": "5.0.0-rc.2", "private": true, "devDependencies": { "@babel/core": "~7.22.20", - "@rocket.chat/fuselage": "^0.55.2", + "@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/ui-contexts": "workspace:^", "@types/babel__core": "~7.20.3", "@types/react": "~17.0.69", @@ -31,7 +31,7 @@ ], "peerDependencies": { "@rocket.chat/fuselage": "*", - "@rocket.chat/ui-contexts": "8.0.0", + "@rocket.chat/ui-contexts": "9.0.0-rc.2", "react": "~17.0.2" }, "volta": { diff --git a/packages/ui-client/CHANGELOG.md b/packages/ui-client/CHANGELOG.md index 2da8d9c6ca9a3..a08ff45507d97 100644 --- a/packages/ui-client/CHANGELOG.md +++ b/packages/ui-client/CHANGELOG.md @@ -1,5 +1,56 @@ # @rocket.chat/ui-client +## 9.0.0-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.2 +
+ +## 9.0.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.1 +
+ +## 9.0.0-rc.0 + +### Minor Changes + +- ([#32517](https://github.com/RocketChat/Rocket.Chat/pull/32517)) Feature Preview: New Navigation - `Header` and `Contextualbar` size improvements consistent with the new global `NavBar` + +### Patch Changes + +- ([#32024](https://github.com/RocketChat/Rocket.Chat/pull/32024)) Implemented a new tab to the users page called 'Active', this tab lists all users who have logged in for the first time and are active. + +-
Updated dependencies [2d89a0c448, 4e8aa575a6]: + + - @rocket.chat/ui-contexts@9.0.0-rc.0 +
+ +## 8.0.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.2 +
+ +## 8.0.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.1 +
+ ## 8.0.0 ### Minor Changes diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index fe35430364cf2..0e0b3778993ce 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -1,12 +1,12 @@ { "name": "@rocket.chat/ui-client", - "version": "8.0.0", + "version": "9.0.0-rc.2", "private": true, "devDependencies": { "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.55.2", + "@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "^0.36.0", "@rocket.chat/mock-providers": "workspace:^", @@ -26,7 +26,7 @@ "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^8.0.1", "@types/babel__core": "~7.20.3", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "@types/react": "~17.0.69", "@types/react-dom": "~17.0.22", "eslint": "~8.45.0", @@ -35,7 +35,7 @@ "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.15", "eslint-plugin-testing-library": "~5.11.1", - "jest": "~29.6.4", + "jest": "~29.7.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-hook-form": "~7.45.4", @@ -63,7 +63,7 @@ "@rocket.chat/fuselage": "*", "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", - "@rocket.chat/ui-contexts": "8.0.0", + "@rocket.chat/ui-contexts": "9.0.0-rc.2", "react": "~17.0.2" }, "volta": { diff --git a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx index affe467cd9659..317f56899d05a 100644 --- a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx +++ b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustom.tsx @@ -1,7 +1,7 @@ -import { Box } from '@rocket.chat/fuselage'; +import { Box, Button } from '@rocket.chat/fuselage'; import { useOutsideClick, useToggle } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import type { Dispatch, FormEvent, ReactElement, RefObject, SetStateAction } from 'react'; +import type { ComponentProps, FormEvent, ReactElement, RefObject } from 'react'; import { useCallback, useRef } from 'react'; import MultiSelectCustomAnchor from './MultiSelectCustomAnchor'; @@ -21,22 +21,12 @@ const onMouseEventPreventSideEffects = (e: MouseEvent): void => { e.stopImmediatePropagation(); }; -type TitleOptionProp = { +export type OptionProp = { id: string; text: string; - isGroupTitle: boolean; - checked: never; + checked?: boolean; }; -type CheckboxOptionProp = { - id: string; - text: string; - isGroupTitle: never; - checked: boolean; -}; - -export type OptionProp = TitleOptionProp | CheckboxOptionProp; - /** * @param dropdownOptions options available for the multiselect dropdown list * @param defaultTitle dropdown text before selecting any options (or all of them). For example: 'All rooms' @@ -56,9 +46,9 @@ type DropDownProps = { defaultTitle: TranslationKey; selectedOptionsTitle: TranslationKey; selectedOptions: OptionProp[]; - setSelectedOptions: Dispatch>; + setSelectedOptions: (roles: OptionProp[]) => void; searchBarText?: TranslationKey; -}; +} & ComponentProps; export const MultiSelectCustom = ({ dropdownOptions, @@ -67,6 +57,7 @@ export const MultiSelectCustom = ({ selectedOptions, setSelectedOptions, searchBarText, + ...props }: DropDownProps): ReactElement => { const reference = useRef(null); const target = useRef(null); @@ -102,7 +93,7 @@ export const MultiSelectCustom = ({ const count = dropdownOptions.filter((option) => option.checked).length; return ( - + {collapsed && ( diff --git a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomAnchor.tsx b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomAnchor.tsx index 0a8aee69344bd..3a03673bc701d 100644 --- a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomAnchor.tsx +++ b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomAnchor.tsx @@ -14,7 +14,7 @@ type MultiSelectCustomAnchorProps = { } & ComponentProps; const MultiSelectCustomAnchor = forwardRef(function MultiSelectCustomAnchor( - { collapsed, selectedOptionsCount, selectedOptionsTitle, defaultTitle, maxCount, ...props }, + { className, collapsed, selectedOptionsCount, selectedOptionsTitle, defaultTitle, maxCount, ...props }, ref, ) { const t = useTranslation(); @@ -34,7 +34,7 @@ const MultiSelectCustomAnchor = forwardRef {isDirty ? `${t(selectedOptionsTitle)} (${selectedOptionsCount})` : t(defaultTitle)} diff --git a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomList.tsx b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomList.tsx index d8f8d60d80961..71cb54f81aa57 100644 --- a/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomList.tsx +++ b/packages/ui-client/src/components/MultiSelectCustom/MultiSelectCustomList.tsx @@ -40,11 +40,7 @@ const MultiSelectCustomList = ({ )} {filteredOptions.map((option) => ( - {option.isGroupTitle ? ( - - {t(option.text as TranslationKey)} - - ) : ( + {option.hasOwnProperty('checked') ? ( + ) : ( + + {t(option.text as TranslationKey)} + )} ))} diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index fdc00ad086e16..485f4d7ac725d 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -2,11 +2,25 @@ "name": "@rocket.chat/ui-composer", "version": "0.2.0", "private": true, + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "files": [ + "/dist" + ], + "scripts": { + "lint": "eslint --ext .js,.jsx,.ts,.tsx .", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", + "test": "jest", + "build": "rm -rf dist && tsc -p tsconfig.build.json", + "typecheck": "tsc --noEmit", + "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput", + "storybook": "start-storybook -p 6006" + }, "devDependencies": { "@babel/core": "~7.22.20", "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.55.2", + "@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/icons": "^0.36.0", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", @@ -16,13 +30,17 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.3", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", + "@types/react": "~17.0.69", + "@types/react-dom": "~17.0.22", "eslint": "~8.45.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.15", - "jest": "~29.6.4", + "jest": "~29.7.0", + "react": "~17.0.2", "react-docgen-typescript-plugin": "~1.0.5", + "react-dom": "~17.0.2", "ts-jest": "~29.1.1", "typescript": "~5.3.3" }, @@ -33,19 +51,6 @@ "react": "^17.0.2", "react-dom": "^17.0.2" }, - "scripts": { - "lint": "eslint --ext .js,.jsx,.ts,.tsx .", - "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", - "test": "jest", - "build": "rm -rf dist && tsc -p tsconfig.json", - "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput", - "storybook": "start-storybook -p 6006" - }, - "main": "./dist/index.js", - "typings": "./dist/index.d.ts", - "files": [ - "/dist" - ], "volta": { "extends": "../../package.json" } diff --git a/packages/ui-composer/tsconfig.build.json b/packages/ui-composer/tsconfig.build.json new file mode 100644 index 0000000000000..1201960b9fb10 --- /dev/null +++ b/packages/ui-composer/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["./src/**/*.stories.tsx"] +} diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 90e106d411f1a..f2bbb77a501cc 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,5 +1,68 @@ # @rocket.chat/ui-contexts +## 9.0.0-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.2 + - @rocket.chat/rest-typings@6.11.0-rc.2 + - @rocket.chat/ddp-client@0.3.3-rc.2 +
+ +## 9.0.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.11.0-rc.1 + - @rocket.chat/rest-typings@6.11.0-rc.1 + - @rocket.chat/ddp-client@0.3.2-rc.1 +
+ +## 9.0.0-rc.0 + +### Minor Changes + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. + +### Patch Changes + +- ([#32482](https://github.com/RocketChat/Rocket.Chat/pull/32482)) Fixed an issue with blocked login when dismissed 2FA modal by clicking outside of it or pressing the escape key + +-
Updated dependencies [b4bbcbfc9a, 25da5280a5, 1b7b1161cf, 03c8b066f9, 2d89a0c448, 24f7df4894, 3ffe4a2944, 3b4b19cfc5, 264d7d5496, b8e5887fb9]: + + - @rocket.chat/i18n@0.6.0-rc.0 + - @rocket.chat/core-typings@6.11.0-rc.0 + - @rocket.chat/rest-typings@6.11.0-rc.0 + - @rocket.chat/ddp-client@0.3.2-rc.0 +
+ +## 8.0.2 + +### Patch Changes + +-
Updated dependencies [ca6a9d8de8, ca6a9d8de8, ca6a9d8de8, ca6a9d8de8]: + + - @rocket.chat/core-typings@6.10.2 + - @rocket.chat/rest-typings@6.10.2 + - @rocket.chat/ddp-client@0.3.2 +
+ +## 8.0.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/core-typings@6.10.1 + - @rocket.chat/rest-typings@6.10.1 + - @rocket.chat/ddp-client@0.3.1 +
+ ## 8.0.0 ### Minor Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index c7d7e31780f3b..c98c051476629 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,20 +1,21 @@ { "name": "@rocket.chat/ui-contexts", - "version": "8.0.0", + "version": "9.0.0-rc.2", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", + "@rocket.chat/ddp-client": "workspace:~", "@rocket.chat/emitter": "~0.31.25", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/i18n": "workspace:~", "@rocket.chat/rest-typings": "workspace:^", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "@types/react": "~17.0.69", "@types/react-dom": "~17.0.22", "@types/use-sync-external-store": "^0.0.5", "eslint": "~8.45.0", "eslint-plugin-react-hooks": "^4.6.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "mongodb": "^4.17.2", "react": "~17.0.2", "ts-jest": "~29.1.1", diff --git a/packages/ui-contexts/src/RouterContext.ts b/packages/ui-contexts/src/RouterContext.ts index 3fbd4bd98cf21..5ed0bbec823a9 100644 --- a/packages/ui-contexts/src/RouterContext.ts +++ b/packages/ui-contexts/src/RouterContext.ts @@ -1,3 +1,4 @@ +import type { RoomType, RoomRouteData, DirectRoomRouteData, OmnichannelRoomRouteData, ChannelRouteData } from '@rocket.chat/core-typings'; import type { ReactNode } from 'react'; import { createContext } from 'react'; @@ -65,6 +66,15 @@ export type RouterContextValue = { defineRoutes(routes: RouteObject[]): () => void; getRoutes(): RouteObject[]; subscribeToRoutesChange(onRoutesChange: () => void): () => void; + getRoomRoute(roomType: 'd', routeData: DirectRoomRouteData): { path: LocationPathname }; + getRoomRoute(roomType: 'l' | 'v', routeData: OmnichannelRoomRouteData): { path: LocationPathname }; + getRoomRoute(roomType: 'p' | 'c', routeData: ChannelRouteData): { path: LocationPathname }; + getRoomRoute( + roomType: RoomType, + routeData: RoomRouteData, + ): { + path: LocationPathname; + }; }; export const RouterContext = createContext({ @@ -92,5 +102,8 @@ export const RouterContext = createContext({ getRoutes: () => { throw new Error('not implemented'); }, + getRoomRoute: () => { + throw new Error('not implemented'); + }, subscribeToRoutesChange: () => () => undefined, }); diff --git a/packages/ui-contexts/src/ServerContext/ServerContext.ts b/packages/ui-contexts/src/ServerContext/ServerContext.ts index 14a2b0e1a3ea9..e5b7fd63c1c5d 100644 --- a/packages/ui-contexts/src/ServerContext/ServerContext.ts +++ b/packages/ui-contexts/src/ServerContext/ServerContext.ts @@ -1,6 +1,12 @@ import type { IServerInfo, Serialized } from '@rocket.chat/core-typings'; -import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from '@rocket.chat/ddp-client/src/types/methods'; -import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; +import type { + ServerMethodName, + ServerMethodParameters, + ServerMethodReturn, + StreamKeys, + StreamNames, + StreamerCallbackArgs, +} from '@rocket.chat/ddp-client'; import type { Method, OperationParams, OperationResult, PathFor, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; import { createContext } from 'react'; diff --git a/packages/ui-contexts/src/hooks/useGoToRoom.ts b/packages/ui-contexts/src/hooks/useGoToRoom.ts new file mode 100644 index 0000000000000..e809a71e25bbe --- /dev/null +++ b/packages/ui-contexts/src/hooks/useGoToRoom.ts @@ -0,0 +1,29 @@ +import type { IRoom } from '@rocket.chat/core-typings'; +import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; + +import { useEndpoint } from './useEndpoint'; +import { useRouter } from './useRouter'; + +export const useGoToRoom = ({ replace = false }: { replace?: boolean } = {}): ((rid: IRoom['_id']) => void) => { + const router = useRouter(); + const getRoomById = useEndpoint('GET', '/v1/rooms.info'); + + return useEffectEvent(async (roomId) => { + const { room } = await getRoomById({ roomId }); + + if (!room) { + return; + } + + const { t, name, _id: rid } = room; + + const { path } = router.getRoomRoute(t, ['c', 'p'].includes(t) ? { name } : { rid }); + + router.navigate( + { + pathname: path, + }, + { replace }, + ); + }); +}; diff --git a/packages/ui-contexts/src/hooks/useMethod.ts b/packages/ui-contexts/src/hooks/useMethod.ts index 0d71f28839721..a9f1513ed2f32 100644 --- a/packages/ui-contexts/src/hooks/useMethod.ts +++ b/packages/ui-contexts/src/hooks/useMethod.ts @@ -1,8 +1,12 @@ -import type { ServerMethodFunction, ServerMethodParameters, ServerMethods } from '@rocket.chat/ddp-client/src/types/methods'; +import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn, ServerMethods } from '@rocket.chat/ddp-client'; import { useCallback, useContext } from 'react'; import { ServerContext } from '../ServerContext'; +type ServerMethodFunction = ( + ...args: ServerMethodParameters +) => Promise>; + /* @deprecated prefer the use of api endpoints (useEndpoint) */ export const useMethod = (methodName: MethodName): ServerMethodFunction => { const { callMethod } = useContext(ServerContext); diff --git a/packages/ui-contexts/src/hooks/useStream.ts b/packages/ui-contexts/src/hooks/useStream.ts index d6fb5e76be787..cb008eea468a7 100644 --- a/packages/ui-contexts/src/hooks/useStream.ts +++ b/packages/ui-contexts/src/hooks/useStream.ts @@ -1,4 +1,4 @@ -import type { StreamNames, StreamerEvents, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; +import type { StreamNames, StreamerEvents, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ddp-client'; import { useContext, useMemo } from 'react'; import { ServerContext } from '../ServerContext'; diff --git a/packages/ui-contexts/src/hooks/useUserSubscriptions.ts b/packages/ui-contexts/src/hooks/useUserSubscriptions.ts index e59a1e733a2f5..ab5cdb9baacd1 100644 --- a/packages/ui-contexts/src/hooks/useUserSubscriptions.ts +++ b/packages/ui-contexts/src/hooks/useUserSubscriptions.ts @@ -1,9 +1,9 @@ -import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts/src/types/SubscriptionWithRoom'; import { useContext, useMemo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; import type { FindOptions, SubscriptionQuery } from '../UserContext'; import { UserContext } from '../UserContext'; +import type { SubscriptionWithRoom } from '../types/SubscriptionWithRoom'; export const useUserSubscriptions = (query: SubscriptionQuery, options?: FindOptions): SubscriptionWithRoom[] => { const { querySubscriptions } = useContext(UserContext); diff --git a/packages/ui-contexts/src/index.ts b/packages/ui-contexts/src/index.ts index 0870eb4417c8a..e021eb840aa0d 100644 --- a/packages/ui-contexts/src/index.ts +++ b/packages/ui-contexts/src/index.ts @@ -30,6 +30,7 @@ export { useCurrentModal } from './hooks/useCurrentModal'; export { useCurrentRoutePath } from './hooks/useCurrentRoutePath'; export { useCustomSound } from './hooks/useCustomSound'; export { useEndpoint } from './hooks/useEndpoint'; +export { useGoToRoom } from './hooks/useGoToRoom'; export type { EndpointFunction } from './hooks/useEndpoint'; export { useIsPrivilegedSettingsContext } from './hooks/useIsPrivilegedSettingsContext'; export { useIsSettingsContextLoading } from './hooks/useIsSettingsContextLoading'; @@ -91,21 +92,6 @@ export { useSetOutputMediaDevice } from './hooks/useSetOutputMediaDevice'; export { useSetInputMediaDevice } from './hooks/useSetInputMediaDevice'; export { useAccountsCustomFields } from './hooks/useAccountsCustomFields'; -export { - ServerMethods, - ServerMethodName, - ServerMethodParameters, - ServerMethodReturn, - ServerMethodFunction, -} from '@rocket.chat/ddp-client/src/types/methods'; -export { - StreamerEvents, - StreamNames, - StreamKeys, - StreamerConfigs, - StreamerConfig, - StreamerCallbackArgs, -} from '@rocket.chat/ddp-client/src/types/streams'; export { UploadResult } from './ServerContext'; export { TranslationKey, TranslationLanguage } from './TranslationContext'; export { Fields } from './UserContext'; diff --git a/packages/ui-kit/CHANGELOG.md b/packages/ui-kit/CHANGELOG.md index 1e920681e13dd..c82da29ef930e 100644 --- a/packages/ui-kit/CHANGELOG.md +++ b/packages/ui-kit/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 0.36.0-rc.0 + +### Minor Changes + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. + ## 0.35.0 ### Minor Changes diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index b4819ef4311cf..4c03f96828c6f 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-kit", - "version": "0.35.0", + "version": "0.36.0-rc.0", "description": "Interactive UI elements for Rocket.Chat Apps", "homepage": "https://rocket.chat", "author": { @@ -40,10 +40,10 @@ "@babel/plugin-transform-runtime": "~7.21.4", "@babel/preset-env": "~7.21.4", "@rocket.chat/eslint-config": "workspace:~", - "@types/jest": "~29.5.0", + "@types/jest": "~29.5.12", "babel-loader": "~9.1.2", "eslint": "~8.45.0", - "jest": "~29.5.0", + "jest": "~29.7.0", "npm-run-all": "~4.1.5", "prettier": "~2.8.8", "rimraf": "~3.0.2", diff --git a/packages/ui-video-conf/.storybook/main.js b/packages/ui-video-conf/.storybook/main.js index 6919b887faafd..dc95f3584a3f9 100644 --- a/packages/ui-video-conf/.storybook/main.js +++ b/packages/ui-video-conf/.storybook/main.js @@ -1,7 +1,7 @@ /** @type {import('@storybook/react/types').StorybookConfig} */ module.exports = { stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-essentials'], + addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'], framework: '@storybook/react', features: { postcss: false, diff --git a/packages/ui-video-conf/.storybook/preview.js b/packages/ui-video-conf/.storybook/preview.js index 5980deceb25b3..d1e44f0c2b8c0 100644 --- a/packages/ui-video-conf/.storybook/preview.js +++ b/packages/ui-video-conf/.storybook/preview.js @@ -1,5 +1,6 @@ import '../../../apps/meteor/app/theme/client/main.css'; import 'highlight.js/styles/github.css'; +import '@rocket.chat/icons/dist/rocketchat.css'; export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, diff --git a/packages/ui-video-conf/CHANGELOG.md b/packages/ui-video-conf/CHANGELOG.md index 0eecd8aa30726..4acd711bd304b 100644 --- a/packages/ui-video-conf/CHANGELOG.md +++ b/packages/ui-video-conf/CHANGELOG.md @@ -1,5 +1,60 @@ # @rocket.chat/ui-video-conf +## 9.0.0-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.2 + - @rocket.chat/ui-avatar@5.0.0-rc.2 +
+ +## 9.0.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.1 + - @rocket.chat/ui-avatar@5.0.0-rc.1 +
+ +## 9.0.0-rc.0 + +### Minor Changes + +- ([#32793](https://github.com/RocketChat/Rocket.Chat/pull/32793)) New Feature: Video Conference Persistent Chat. + This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat. + +### Patch Changes + +-
Updated dependencies [2d89a0c448, 4e8aa575a6]: + + - @rocket.chat/ui-contexts@9.0.0-rc.0 + - @rocket.chat/ui-avatar@5.0.0-rc.0 +
+ +## 8.0.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.2 + - @rocket.chat/ui-avatar@4.0.2 +
+ +## 8.0.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.1 + - @rocket.chat/ui-avatar@4.0.1 +
+ ## 8.0.0 ### Minor Changes diff --git a/packages/ui-video-conf/jest.config.ts b/packages/ui-video-conf/jest.config.ts new file mode 100644 index 0000000000000..14379928638fc --- /dev/null +++ b/packages/ui-video-conf/jest.config.ts @@ -0,0 +1,8 @@ +import client from '@rocket.chat/jest-presets/client'; +import type { Config } from 'jest'; + +export default { + preset: client.preset, + setupFilesAfterEnv: [...client.setupFilesAfterEnv], + testMatch: ['/src/**/*.spec.{ts,tsx}'], +} satisfies Config; diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index efacce7a45869..c0bb5adb1cac2 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -1,17 +1,19 @@ { "name": "@rocket.chat/ui-video-conf", - "version": "8.0.0", + "version": "9.0.0-rc.2", "private": true, "devDependencies": { "@babel/core": "~7.22.20", "@rocket.chat/css-in-js": "~0.31.25", "@rocket.chat/eslint-config": "workspace:^", - "@rocket.chat/fuselage": "^0.55.2", + "@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/icons": "^0.36.0", + "@rocket.chat/jest-presets": "workspace:~", "@rocket.chat/styled": "~0.31.25", "@rocket.chat/ui-avatar": "workspace:^", "@rocket.chat/ui-contexts": "workspace:^", + "@storybook/addon-a11y": "^6.5.16", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", "@storybook/addon-essentials": "~6.5.16", @@ -19,13 +21,16 @@ "@storybook/manager-webpack4": "~6.5.16", "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", + "@storybook/testing-react": "~1.3.0", "@types/babel__core": "~7.20.3", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", + "@types/jest-axe": "~3.5.9", "eslint": "~8.45.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.15", - "jest": "~29.6.4", + "jest": "~29.7.0", + "jest-axe": "~9.0.0", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.1.1", "typescript": "~5.3.3" @@ -36,8 +41,8 @@ "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/icons": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-avatar": "4.0.0", - "@rocket.chat/ui-contexts": "8.0.0", + "@rocket.chat/ui-avatar": "5.0.0-rc.2", + "@rocket.chat/ui-contexts": "9.0.0-rc.2", "react": "^17.0.2", "react-dom": "^17.0.2" }, @@ -45,9 +50,9 @@ "eslint": "eslint --ext .js,.jsx,.ts,.tsx .", "eslint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "test": "jest", - "build": "tsc -p tsconfig.json", + "build": "tsc -p tsconfig.build.json", "storybook": "start-storybook -p 6006", - "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" + "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput" }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", diff --git a/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.spec.tsx b/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.spec.tsx new file mode 100644 index 0000000000000..a55b22c4a8654 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.spec.tsx @@ -0,0 +1,19 @@ +import { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import { axe } from 'jest-axe'; + +import * as stories from './VideoConfButton.stories'; + +const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); + +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const tree = render(); + expect(tree.baseElement).toMatchSnapshot(); +}); + +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); diff --git a/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.stories.tsx b/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.stories.tsx new file mode 100644 index 0000000000000..769f98563d6ed --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.stories.tsx @@ -0,0 +1,10 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; + +import VideoConfButton from './VideoConfButton'; + +export default { + title: 'Components/VideoConfButton', + component: VideoConfButton, +} satisfies ComponentMeta; + +export const Default: ComponentStory = () => Button; diff --git a/packages/ui-video-conf/src/VideoConfButton.tsx b/packages/ui-video-conf/src/VideoConfButton/VideoConfButton.tsx similarity index 100% rename from packages/ui-video-conf/src/VideoConfButton.tsx rename to packages/ui-video-conf/src/VideoConfButton/VideoConfButton.tsx diff --git a/packages/ui-video-conf/src/VideoConfButton/__snapshots__/VideoConfButton.spec.tsx.snap b/packages/ui-video-conf/src/VideoConfButton/__snapshots__/VideoConfButton.spec.tsx.snap new file mode 100644 index 0000000000000..b7170a3c0deed --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfButton/__snapshots__/VideoConfButton.spec.tsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders Default without crashing 1`] = ` + +
+ +
+ +`; diff --git a/packages/ui-video-conf/src/VideoConfButton/index.ts b/packages/ui-video-conf/src/VideoConfButton/index.ts new file mode 100644 index 0000000000000..7a3637f70e314 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfButton/index.ts @@ -0,0 +1 @@ +export { default } from './VideoConfButton'; diff --git a/packages/ui-video-conf/src/VideoConfController/VideoConfController.spec.tsx b/packages/ui-video-conf/src/VideoConfController/VideoConfController.spec.tsx new file mode 100644 index 0000000000000..96f5b50af1e36 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfController/VideoConfController.spec.tsx @@ -0,0 +1,19 @@ +import { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import { axe } from 'jest-axe'; + +import * as stories from './VideoConfController.stories'; + +const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); + +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const tree = render(); + expect(tree.baseElement).toMatchSnapshot(); +}); + +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); diff --git a/packages/ui-video-conf/src/VideoConfController/VideoConfController.stories.tsx b/packages/ui-video-conf/src/VideoConfController/VideoConfController.stories.tsx new file mode 100644 index 0000000000000..dcce6da237ae8 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfController/VideoConfController.stories.tsx @@ -0,0 +1,14 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; + +import VideoConfController from './VideoConfController'; + +export default { + title: 'Components/VideoConfController', + component: VideoConfController, +} satisfies ComponentMeta; + +export const Default: ComponentStory = (args) => ; +Default.args = { + 'icon': 'info', + 'aria-label': 'info', +}; diff --git a/packages/ui-video-conf/src/VideoConfController.tsx b/packages/ui-video-conf/src/VideoConfController/VideoConfController.tsx similarity index 65% rename from packages/ui-video-conf/src/VideoConfController.tsx rename to packages/ui-video-conf/src/VideoConfController/VideoConfController.tsx index b3813e329535b..0b339fa0baec6 100644 --- a/packages/ui-video-conf/src/VideoConfController.tsx +++ b/packages/ui-video-conf/src/VideoConfController/VideoConfController.tsx @@ -1,5 +1,4 @@ import { IconButton } from '@rocket.chat/fuselage'; -import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ButtonHTMLAttributes } from 'react'; @@ -11,21 +10,16 @@ type VideoConfControllerProps = { small?: boolean; } & Omit, 'ref' | 'is' | 'className' | 'size' | 'elevation'>; -const VideoConfController = ({ icon, active, secondary, disabled, small = true, ...props }: VideoConfControllerProps): ReactElement => { - const id = useUniqueId(); - - return ( - - ); -}; +const VideoConfController = ({ icon, active, secondary, disabled, small = true, ...props }: VideoConfControllerProps): ReactElement => ( + +); export default VideoConfController; diff --git a/packages/ui-video-conf/src/VideoConfController/__snapshots__/VideoConfController.spec.tsx.snap b/packages/ui-video-conf/src/VideoConfController/__snapshots__/VideoConfController.spec.tsx.snap new file mode 100644 index 0000000000000..a9c09dcdf1660 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfController/__snapshots__/VideoConfController.spec.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders Default without crashing 1`] = ` + +
+ +
+ +`; diff --git a/packages/ui-video-conf/src/VideoConfController/index.ts b/packages/ui-video-conf/src/VideoConfController/index.ts new file mode 100644 index 0000000000000..352b2fb9769b8 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfController/index.ts @@ -0,0 +1 @@ +export { default } from './VideoConfController'; diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.spec.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.spec.tsx new file mode 100644 index 0000000000000..6b0db6b13b336 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.spec.tsx @@ -0,0 +1,19 @@ +import { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import { axe } from 'jest-axe'; + +import * as stories from './VideoConfMessage.stories'; + +const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); + +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const tree = render(); + expect(tree.baseElement).toMatchSnapshot(); +}); + +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.stories.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.stories.tsx index 121db9cac51d3..81a42d8b458fb 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.stories.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.stories.tsx @@ -2,7 +2,6 @@ import { MessageDivider, Message, Avatar, Box } from '@rocket.chat/fuselage'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import type { ReactElement } from 'react'; -import '@rocket.chat/icons/dist/rocketchat.css'; import { VideoConfMessage, VideoConfMessageIcon, VideoConfMessageRow, VideoConfMessageText } from '.'; import VideoConfMessageAction from './VideoConfMessageAction'; import VideoConfMessageActions from './VideoConfMessageActions'; @@ -22,7 +21,7 @@ export default { May, 24, 2020 - + @@ -41,7 +40,7 @@ export default {
), ], -} as ComponentMeta; +} satisfies ComponentMeta; const avatarUrl = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z'; @@ -54,7 +53,7 @@ export const CallingDM: ComponentStory = () => ( Calling... - + @@ -72,7 +71,7 @@ export const CallEndedDM: ComponentStory = () => ( Call ended - + @@ -90,7 +89,7 @@ export const CallOngoing: ComponentStory = () => ( Call ongoing - + @@ -109,7 +108,7 @@ export const CallEnded: ComponentStory = () => ( Call ended - + diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.tsx index 7c22b707770f1..d873da1413d0c 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessage.tsx @@ -1,7 +1,8 @@ import { Box } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; +import type { AllHTMLAttributes, ReactElement } from 'react'; -const VideoConfMessage = ({ ...props }): ReactElement => ( +type VideoConfMessageProps = Omit, 'is'>; +const VideoConfMessage = (props: VideoConfMessageProps): ReactElement => ( ( borderWidth={1} borderColor='extra-light' borderRadius='x4' - className='rcx-videoconf-message-block' + rcx-videoconf-message-block {...props} /> ); diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageAction.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageAction.tsx index 9b705001c03fa..a50fbda68bf6b 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageAction.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageAction.tsx @@ -2,6 +2,6 @@ import { IconButton } from '@rocket.chat/fuselage'; import type { ComponentProps, ReactElement } from 'react'; const VideoConfMessageAction = ({ icon = 'info', ...props }: ComponentProps): ReactElement => ( - + ); export default VideoConfMessageAction; diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageActions.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageActions.tsx index cc6112dfbd35f..9556c334501bb 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageActions.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageActions.tsx @@ -2,7 +2,7 @@ import { ButtonGroup } from '@rocket.chat/fuselage'; import type { ComponentProps, ReactElement } from 'react'; const VideoConfMessageActions = ({ children, ...props }: ComponentProps): ReactElement => ( - + {children} ); diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageButton.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageButton.tsx index 75d2933bbce0e..44463b2d28dbd 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageButton.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageButton.tsx @@ -1,15 +1,9 @@ -import { Box, Button } from '@rocket.chat/fuselage'; -import type { AllHTMLAttributes, ReactElement, ReactNode } from 'react'; +import { Button } from '@rocket.chat/fuselage'; +import type { AllHTMLAttributes, ReactElement } from 'react'; -const VideoConfMessageButton = ({ - children, - primary, - ...props -}: { children: ReactNode; primary?: boolean } & Omit, 'is'>): ReactElement => ( - - - +type VideoConfMessageButtonProps = { primary?: boolean } & Omit, 'is'>; + +const VideoConfMessageButton = ({ primary, ...props }: VideoConfMessageButtonProps): ReactElement => ( + + + +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+

+ Joined +

+
+
+ + + + + + + +`; + +exports[`renders CallEndedDM without crashing 1`] = ` + +
+
+ + +`; + +exports[`renders CallOngoing without crashing 1`] = ` + +
+
+ + +`; + +exports[`renders CallingDM without crashing 1`] = ` + +
+
+ + +`; + +exports[`renders Loading without crashing 1`] = ` + +
+
+ + +`; diff --git a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.spec.tsx b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.spec.tsx new file mode 100644 index 0000000000000..d51db2e964d84 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.spec.tsx @@ -0,0 +1,19 @@ +import { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import { axe } from 'jest-axe'; + +import * as stories from './VideoConfPopup.stories'; + +const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); + +test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { + const tree = render(); + expect(tree.baseElement).toMatchSnapshot(); +}); + +test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); diff --git a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.stories.tsx b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.stories.tsx index 4f73e3cf47758..c32590af8a14f 100644 --- a/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.stories.tsx +++ b/packages/ui-video-conf/src/VideoConfPopup/VideoConfPopup.stories.tsx @@ -3,7 +3,6 @@ import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import type { ReactElement } from 'react'; -import '@rocket.chat/icons/dist/rocketchat.css'; import { VideoConfPopup, VideoConfPopupHeader, @@ -27,22 +26,22 @@ export default { ), ], -} as ComponentMeta; +} satisfies ComponentMeta; const avatarUrl = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAAAAAAABAgMABAUREiExBhMUIkFRYQcWcYGhFTJSgpH/xAAYAQADAQEAAAAAAAAAAAAAAAACAwQBAP/EAB4RAAIBBQEBAQAAAAAAAAAAAAABAgMREiExE0HR/9oADAMBAAIRAxEAPwBuXuIkhBuMe5ib/AHQP49q4L3mLitryTLTSpOiHQI5k/HzXa/qbFOEudVTu1dumWvcTaNCZYZ7vU6g6LxqjOU/24dfs1Ouh9FnkMpd3Reeyx83hAxZZEhkdV9/MBrX71WGPvJcqrJBGveKATtuXXqNU0pu02bTHXD/AGvJAluyxxRd6F4x00o+NdKoVrjbzJdvVe1t5cVLc2ck8qjnohgpPtz2v7G6JtPQ2VJwjlcw+37mchpnK6GtIuv5NFWeTsLNPvxWTvpfjvOEfwKKzEVkSct2vscS/BIzSN0YRkeX81UpPqO8masJETu7OOccY4dswYFQeftv096XV5knuJGdm2T1+agvMXj8jEaHX905QihabvcbuS7X566mLWLwSY8PuRnk/u4eZ0deTl71Ef6hY+0yM88TzeNZY4luYwpVYyduOfrvhPTnr0pXSX9y5mCsyJMdyxxvwq599em+taItqCSNc90ChvZRUruUcT0JiO18Elpk7t8v41LWzacxkBSuvjQ/FFJayjDWrCTepAQ2vUH0oo/Jk3ovpwJJeVCP5CN+lFFaaMqy+nAyuChvrTI2kN9JAsi2ZOy4IBHMnkSCP+iqBexSWdxLazoUljJVlPUH2oorkV10pRc7b1zXb/hZOzuJvM86QWEXeELxOzHSIPcmiiiunVlF2RNTpRkrs//Z'; export const StartCall: ComponentStory = () => ( - + - - + + - } icon={}> + } icon={}> Room Name diff --git a/packages/ui-video-conf/src/VideoConfPopup/__snapshots__/VideoConfPopup.spec.tsx.snap b/packages/ui-video-conf/src/VideoConfPopup/__snapshots__/VideoConfPopup.spec.tsx.snap new file mode 100644 index 0000000000000..358cec7d7f7dd --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfPopup/__snapshots__/VideoConfPopup.spec.tsx.snap @@ -0,0 +1,117 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders StartCall without crashing 1`] = ` + +
+
+ +
+
+ +`; diff --git a/packages/ui-video-conf/src/hooks/useVideoConfControllers.ts b/packages/ui-video-conf/src/hooks/useVideoConfControllers.ts index 8e55ffa24d798..41bc48b97a25b 100644 --- a/packages/ui-video-conf/src/hooks/useVideoConfControllers.ts +++ b/packages/ui-video-conf/src/hooks/useVideoConfControllers.ts @@ -10,13 +10,9 @@ export const useVideoConfControllers = ( ): { controllersConfig: controllersConfigProps; handleToggleMic: () => void; handleToggleCam: () => void } => { const [controllersConfig, setControllersConfig] = useState(initialPreferences); - const handleToggleMic = useCallback((): void => { - setControllersConfig((prevState) => ({ ...prevState, mic: !prevState.mic })); - }, []); + const handleToggleMic = useCallback(() => setControllersConfig((prevState) => ({ ...prevState, mic: !prevState.mic })), []); - const handleToggleCam = useCallback((): void => { - setControllersConfig((prevState) => ({ ...prevState, cam: !prevState.cam })); - }, []); + const handleToggleCam = useCallback(() => setControllersConfig((prevState) => ({ ...prevState, cam: !prevState.cam })), []); return { controllersConfig, diff --git a/packages/ui-video-conf/tsconfig.build.json b/packages/ui-video-conf/tsconfig.build.json new file mode 100644 index 0000000000000..36dbe9c166ff5 --- /dev/null +++ b/packages/ui-video-conf/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["./src/**/*"], + "exclude": ["./src/**/*.stories.tsx", "./src/**/*.spec.ts", "./src/**/*.spec.tsx"] +} diff --git a/packages/ui-video-conf/tsconfig.json b/packages/ui-video-conf/tsconfig.json index e2be47cf5499f..aa282e17ea6cd 100644 --- a/packages/ui-video-conf/tsconfig.json +++ b/packages/ui-video-conf/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "../../tsconfig.base.client.json", "compilerOptions": { - "rootDir": "./src", + "rootDirs": ["./src", "./"], "outDir": "./dist" }, - "include": ["./src/**/*"] + "include": ["./src/**/*", "./jest.config.ts"], } diff --git a/packages/uikit-playground/CHANGELOG.md b/packages/uikit-playground/CHANGELOG.md index 43bc6d9354888..d1aa257adf5c8 100644 --- a/packages/uikit-playground/CHANGELOG.md +++ b/packages/uikit-playground/CHANGELOG.md @@ -1,5 +1,60 @@ # @rocket.chat/uikit-playground +## 0.3.3-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.2 + - @rocket.chat/ui-contexts@9.0.0-rc.2 + - @rocket.chat/ui-avatar@5.0.0-rc.2 +
+ +## 0.3.3-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.1 + - @rocket.chat/ui-contexts@9.0.0-rc.1 + - @rocket.chat/ui-avatar@5.0.0-rc.1 +
+ +## 0.3.3-rc.0 + +### Patch Changes + +-
Updated dependencies [88e5219bd2, 2d89a0c448, 4e8aa575a6, b8e5887fb9]: + + - @rocket.chat/fuselage-ui-kit@9.0.0-rc.0 + - @rocket.chat/ui-contexts@9.0.0-rc.0 + - @rocket.chat/ui-avatar@5.0.0-rc.0 +
+ +## 0.3.2 + +### Patch Changes + +-
Updated dependencies [ca6a9d8de8, ca6a9d8de8, ca6a9d8de8, ca6a9d8de8]: + + - @rocket.chat/fuselage-ui-kit@8.0.2 + - @rocket.chat/ui-contexts@8.0.2 + - @rocket.chat/ui-avatar@4.0.2 +
+ +## 0.3.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/fuselage-ui-kit@8.0.1 + - @rocket.chat/ui-contexts@8.0.1 + - @rocket.chat/ui-avatar@4.0.1 +
+ ## 0.3.0 ### Minor Changes diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index a9293ff218cec..7ffc102699bcf 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/uikit-playground", "private": true, - "version": "0.3.0", + "version": "0.3.3-rc.2", "type": "module", "scripts": { "dev": "vite", @@ -15,10 +15,10 @@ "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", "@rocket.chat/css-in-js": "~0.31.25", - "@rocket.chat/fuselage": "^0.55.2", + "@rocket.chat/fuselage": "^0.57.0", "@rocket.chat/fuselage-hooks": "^0.33.1", "@rocket.chat/fuselage-polyfills": "~0.31.25", - "@rocket.chat/fuselage-toastbar": "^0.32.0", + "@rocket.chat/fuselage-toastbar": "^0.33.0", "@rocket.chat/fuselage-tokens": "^0.33.1", "@rocket.chat/fuselage-ui-kit": "workspace:~", "@rocket.chat/icons": "^0.36.0", diff --git a/packages/web-ui-registration/CHANGELOG.md b/packages/web-ui-registration/CHANGELOG.md index ad91395df6f8f..e6d4fa500e47a 100644 --- a/packages/web-ui-registration/CHANGELOG.md +++ b/packages/web-ui-registration/CHANGELOG.md @@ -1,5 +1,57 @@ # @rocket.chat/web-ui-registration +## 9.0.0-rc.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.2 +
+ +## 9.0.0-rc.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@9.0.0-rc.1 +
+ +## 9.0.0-rc.0 + +### Patch Changes + +- ([#32570](https://github.com/RocketChat/Rocket.Chat/pull/32570)) Login services button was not respecting the button color and text color settings. Implemented a fix to respect these settings and change the button colors accordingly. + + Added a warning on all settings which allow admins to change OAuth button colors, so that they can be alerted about WCAG (Web Content Accessibility Guidelines) compliance. + +- ([#32482](https://github.com/RocketChat/Rocket.Chat/pull/32482)) Fixed an issue with blocked login when dismissed 2FA modal by clicking outside of it or pressing the escape key + +-
Updated dependencies [8fc6ca8b4e, 2d89a0c448, 4e8aa575a6]: + + - @rocket.chat/tools@0.2.2-rc.0 + - @rocket.chat/ui-contexts@9.0.0-rc.0 +
+ +## 8.0.2 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.2 +
+ +## 8.0.1 + +### Patch Changes + +-
Updated dependencies []: + + - @rocket.chat/ui-contexts@8.0.1 +
+ ## 8.0.0 ### Minor Changes diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index a6d3adcfe8f94..1fbc7ee935910 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/web-ui-registration", - "version": "8.0.0", + "version": "9.0.0-rc.2", "private": true, "homepage": "https://rocket.chat", "main": "./dist/index.js", @@ -36,11 +36,11 @@ "@storybook/testing-library": "^0.2.2", "@tanstack/react-query": "^4.16.1", "@testing-library/react": "^13.3.0", - "@types/jest": "~29.5.7", + "@types/jest": "~29.5.12", "@types/react": "~17.0.69", "babel-loader": "~8.3.0", "eslint": "~8.45.0", - "jest": "~29.6.4", + "jest": "~29.7.0", "react": "~17.0.2", "react-hook-form": "~7.45.4", "react-i18next": "~13.2.2", @@ -50,11 +50,14 @@ }, "peerDependencies": { "@rocket.chat/layout": "*", - "@rocket.chat/tools": "0.2.1", - "@rocket.chat/ui-contexts": "8.0.0", + "@rocket.chat/tools": "0.2.2-rc.0", + "@rocket.chat/ui-contexts": "9.0.0-rc.2", "@tanstack/react-query": "*", "react": "*", "react-hook-form": "*", "react-i18next": "*" + }, + "volta": { + "extends": "../../package.json" } } diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index ba16d360d9c1c..d9f43b0e484ca 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -15,8 +15,6 @@ const LoginServicesButton = ({ className, disabled, setError, - buttonColor, - buttonLabelColor, ...props }: T & { className?: string; @@ -45,8 +43,6 @@ const LoginServicesButton = ({ alignItems='center' display='flex' justifyContent='center' - color={buttonLabelColor} - backgroundColor={buttonColor} > {buttonLabelText || t('Sign_in_with__provider__', { provider: title })} diff --git a/packages/web-ui-registration/src/RegisterForm.tsx b/packages/web-ui-registration/src/RegisterForm.tsx index 0eda77879be76..57cf9378ab72a 100644 --- a/packages/web-ui-registration/src/RegisterForm.tsx +++ b/packages/web-ui-registration/src/RegisterForm.tsx @@ -94,14 +94,15 @@ export const RegisterForm = ({ setLoginRoute }: { setLoginRoute: DispatchLoginRo if (error.errorType === 'error-user-already-exists') { setError('username', { type: 'user-already-exists', message: t('registration.component.form.usernameAlreadyExists') }); } - if (/Email already exists/.test(error.error)) { setError('email', { type: 'email-already-exists', message: t('registration.component.form.emailAlreadyExists') }); } - if (/Username is already in use/.test(error.error)) { setError('username', { type: 'username-already-exists', message: t('registration.component.form.userAlreadyExist') }); } + if (/Name contains invalid characters/.test(error.error)) { + setError('name', { type: 'name-contains-invalid-chars', message: t('registration.component.form.nameContainsInvalidChars') }); + } if (/error-too-many-requests/.test(error.error)) { dispatchToastMessage({ type: 'error', message: error.error }); } diff --git a/turbo.json b/turbo.json index f7cf760529c08..7ce868eb37070 100644 --- a/turbo.json +++ b/turbo.json @@ -1,7 +1,7 @@ { "$schema": "https://turborepo.org/schema.json", "globalDependencies": ["tsconfig.base.json", "tsconfig.base.server.json", "tsconfig.base.client.json"], - "pipeline": { + "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] diff --git a/yarn.lock b/yarn.lock index 3b68dcde38064..3f23cb803c93b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,10 +59,10 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.0.1": - version: 4.0.1 - resolution: "@adobe/css-tools@npm:4.0.1" - checksum: 80226e2229024c21da9ffa6b5cd4a34b931f071e06f45aba4777ade071d7a6c94605cf73b13718b0c4b34e8b124c65c607b82eaa53a326d3eb73d9682a04a593 +"@adobe/css-tools@npm:^4.0.1, @adobe/css-tools@npm:^4.4.0": + version: 4.4.0 + resolution: "@adobe/css-tools@npm:4.4.0" + checksum: 1f08fb49bf17fc7f2d1a86d3e739f29ca80063d28168307f1b0a962ef37501c5667271f6771966578897f2e94e43c4770fd802728a6e6495b812da54112d506a languageName: node linkType: hard @@ -2937,16 +2937,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": - version: 7.23.4 - resolution: "@babel/runtime@npm:7.23.4" - dependencies: - regenerator-runtime: ^0.14.0 - checksum: 8eb6a6b2367f7d60e7f7dd83f477cc2e2fdb169e5460694d7614ce5c730e83324bcf29251b70940068e757ad1ee56ff8073a372260d90cad55f18a825caf97cd - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.24.4 resolution: "@babel/runtime@npm:7.24.4" dependencies: @@ -4358,7 +4349,7 @@ __metadata: languageName: node linkType: hard -"@jest/core@npm:^29.5.0, @jest/core@npm:^29.6.4, @jest/core@npm:^29.7.0": +"@jest/core@npm:^29.7.0": version: 29.7.0 resolution: "@jest/core@npm:29.7.0" dependencies: @@ -4399,16 +4390,16 @@ __metadata: languageName: node linkType: hard -"@jest/create-cache-key-function@npm:^27.4.2": - version: 27.5.1 - resolution: "@jest/create-cache-key-function@npm:27.5.1" +"@jest/create-cache-key-function@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/create-cache-key-function@npm:29.7.0" dependencies: - "@jest/types": ^27.5.1 - checksum: a6c3a8c769aca6f66f5dc80f1c77e66980b4f213a6b2a15a92ba3595f032848a1261c06c9c798dcf2b672b1ffbefad5085af89d130548741c85ddbe0cf4284e7 + "@jest/types": ^29.6.3 + checksum: 681bc761fa1d6fa3dd77578d444f97f28296ea80755e90e46d1c8fa68661b9e67f54dd38b988742db636d26cf160450dc6011892cec98b3a7ceb58cad8ff3aae languageName: node linkType: hard -"@jest/environment@npm:^29.6.4, @jest/environment@npm:^29.7.0": +"@jest/environment@npm:^29.7.0": version: 29.7.0 resolution: "@jest/environment@npm:29.7.0" dependencies: @@ -4439,7 +4430,7 @@ __metadata: languageName: node linkType: hard -"@jest/fake-timers@npm:^29.6.4, @jest/fake-timers@npm:^29.7.0": +"@jest/fake-timers@npm:^29.7.0": version: 29.7.0 resolution: "@jest/fake-timers@npm:29.7.0" dependencies: @@ -4627,7 +4618,7 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.5.0, @jest/types@npm:^29.6.3": +"@jest/types@npm:^29.6.3": version: 29.6.3 resolution: "@jest/types@npm:29.6.3" dependencies: @@ -8432,6 +8423,7 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": ~0.31.25 + "@rocket.chat/tools": "workspace:^" "@types/bcrypt": ^5.0.1 "@types/gc-stats": ^1.4.2 "@types/node": ^14.18.63 @@ -8459,9 +8451,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/account-utils@workspace:packages/account-utils" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -8472,13 +8464,13 @@ __metadata: resolution: "@rocket.chat/agenda@workspace:packages/agenda" dependencies: "@types/debug": ^4.1.10 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 cron: ~1.8.2 date.js: ~0.3.3 debug: ~4.1.1 eslint: ~8.45.0 human-interval: ^2.0.1 - jest: ~29.6.4 + jest: ~29.7.0 moment-timezone: ~0.5.43 mongodb: ^4.17.2 ts-jest: ~29.1.1 @@ -8494,11 +8486,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.95 "@swc/jest": ^0.2.29 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/strict-uri-encode": ^2.0.1 eslint: ~8.45.0 filter-obj: ^3.0.0 - jest: ~29.6.4 + jest: ~29.7.0 jest-fetch-mock: ^3.0.3 query-string: ^7.1.3 split-on-first: ^3.0.0 @@ -8509,8 +8501,8 @@ __metadata: linkType: soft "@rocket.chat/apps-engine@npm:alpha": - version: 1.44.0-alpha.814 - resolution: "@rocket.chat/apps-engine@npm:1.44.0-alpha.814" + version: 1.44.0-alpha.828 + resolution: "@rocket.chat/apps-engine@npm:1.44.0-alpha.828" dependencies: "@msgpack/msgpack": 3.0.0-beta2 adm-zip: ^0.5.9 @@ -8526,7 +8518,7 @@ __metadata: uuid: ~8.3.2 peerDependencies: "@rocket.chat/ui-kit": "*" - checksum: 03f777ecd035af20c88558a941db77bb104f4402f78169a6d23e1629613c11ac23c04f6bdfb451a4273558789de58d4567661a7bd5de91b78464f98698e5f6a6 + checksum: e26914b62d2e9823577fe8165a2635d65f69ddc315a880bbc417ddd674e4df487dc9bc9507bf3a0616de06cd927596872c1e90e4c29c61da8581e0a1b7c8d97d languageName: node linkType: hard @@ -8537,9 +8529,9 @@ __metadata: "@rocket.chat/apps-engine": alpha "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/model-typings": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -8587,7 +8579,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.1 "@typescript-eslint/parser": ~5.60.1 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -8597,10 +8589,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/cas-validate@workspace:packages/cas-validate" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 cheerio: 1.0.0-rc.10 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -8624,18 +8616,18 @@ __metadata: "@types/babel__core": ^7.20.3 "@types/babel__preset-env": ^7.9.4 "@types/fibers": ^3.1.3 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 babel-jest: ^29.5.0 eslint: ~8.45.0 fibers: ^5.0.3 - jest: ~29.6.4 + jest: ~29.7.0 mongodb: ^4.17.2 prettier: ~2.8.8 typescript: ~5.3.3 languageName: unknown linkType: soft -"@rocket.chat/core-typings@workspace:^, @rocket.chat/core-typings@workspace:packages/core-typings": +"@rocket.chat/core-typings@workspace:^, @rocket.chat/core-typings@workspace:packages/core-typings, @rocket.chat/core-typings@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/core-typings@workspace:packages/core-typings" dependencies: @@ -8659,9 +8651,9 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/random": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 mongodb: ^4.17.2 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -8690,19 +8682,20 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/ddp-client@workspace:^, @rocket.chat/ddp-client@workspace:ee/packages/ddp-client": +"@rocket.chat/ddp-client@workspace:^, @rocket.chat/ddp-client@workspace:ee/packages/ddp-client, @rocket.chat/ddp-client@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/ddp-client@workspace:ee/packages/ddp-client" dependencies: "@rocket.chat/api-client": "workspace:^" + "@rocket.chat/core-typings": "workspace:~" "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.95 "@swc/jest": ^0.2.29 - "@types/jest": ^29.5.12 + "@types/jest": ~29.5.12 "@types/ws": ^8.5.8 eslint: ~8.45.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 jest-websocket-mock: ^2.4.0 ts-jest: ^29.1.2 typescript: ~5.3.3 @@ -8719,6 +8712,7 @@ __metadata: "@rocket.chat/apps-engine": alpha "@rocket.chat/core-services": "workspace:^" "@rocket.chat/core-typings": "workspace:^" + "@rocket.chat/ddp-client": "workspace:~" "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/instance-status": "workspace:^" @@ -8727,7 +8721,6 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": ~0.31.25 - "@rocket.chat/ui-contexts": "workspace:^" "@types/ejson": ^2.2.1 "@types/gc-stats": ^1.4.2 "@types/meteor": ^2.9.8 @@ -8883,9 +8876,11 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-toastbar@npm:^0.32.0": - version: 0.32.0 - resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0" +"@rocket.chat/fuselage-toastbar@npm:^0.33.0": + version: 0.33.0 + resolution: "@rocket.chat/fuselage-toastbar@npm:0.33.0" + dependencies: + react-timing-hooks: ~4.0.2 peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" @@ -8893,7 +8888,7 @@ __metadata: "@rocket.chat/styled": "*" react: ^17.0.2 react-dom: ^17.0.2 - checksum: 5e78516aee6446da4c76dac10ff83adb4deeb86d6111c42419f0629c35ec64b19ae6ab563f20b5efa2600c9c723b9096edc3c166e960fd25cfda1f07c4df3f3f + checksum: 97993ad2acdc5a742b71c94f9d321e090769c4116ab52208c2103ca41d4455bc4cd15c0cd0a1f8545144910a8c7f7de645ee150c0d8fc6b746001244690de2ed languageName: node linkType: hard @@ -8915,7 +8910,7 @@ __metadata: "@rocket.chat/apps-engine": alpha "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/fuselage-polyfills": ~0.31.25 "@rocket.chat/gazzodown": "workspace:^" @@ -8946,7 +8941,7 @@ __metadata: cross-env: ^7.0.3 eslint: ~8.45.0 i18next: ^23.10.1 - jest: ^29.7.0 + jest: ~29.7.0 normalize.css: ^8.0.1 npm-run-all: ^4.1.5 prettier: ~2.8.8 @@ -8967,19 +8962,19 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-avatar": 4.0.0 - "@rocket.chat/ui-contexts": 8.0.0 - "@rocket.chat/ui-kit": 0.35.0 - "@rocket.chat/ui-video-conf": 8.0.0 + "@rocket.chat/ui-avatar": 5.0.0-rc.2 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 + "@rocket.chat/ui-kit": 0.36.0-rc.0 + "@rocket.chat/ui-video-conf": 9.0.0-rc.2 "@tanstack/react-query": "*" react: "*" react-dom: "*" languageName: unknown linkType: soft -"@rocket.chat/fuselage@npm:^0.55.2": - version: 0.55.2 - resolution: "@rocket.chat/fuselage@npm:0.55.2" +"@rocket.chat/fuselage@npm:^0.57.0": + version: 0.57.0 + resolution: "@rocket.chat/fuselage@npm:0.57.0" dependencies: "@rocket.chat/css-in-js": ^0.31.25 "@rocket.chat/css-supports": ^0.31.25 @@ -8997,7 +8992,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 286f4ac261621a09de74e34ef52f5c473e7c2e55ca977507ab1b1fdccf7274c2ca788a42d1415ffd4f4f629377b0bb1ed9ad70ddead7b46d3b422bac1b861431 + checksum: 80f49f79ca6655067f528a9b427a7bfabbcf1583d0c9f3ed40b79217c7a495e93e0335e15e29b23237bf2ccb5ee5fa063051b40bf4d539439f452ffd45fdb537 languageName: node linkType: hard @@ -9008,7 +9003,7 @@ __metadata: "@babel/core": ~7.22.20 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-tokens": ^0.33.1 "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/styled": ~0.31.25 @@ -9027,7 +9022,7 @@ __metadata: "@swc/jest": ^0.2.29 "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~12.1.5 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/katex": ~0.16.5 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 @@ -9043,8 +9038,8 @@ __metadata: eslint-plugin-storybook: ~0.6.15 highlight.js: ^11.5.1 identity-obj-proxy: ^3.0.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 katex: ~0.16.9 outdent: ^0.8.0 react-docgen-typescript-plugin: ~1.0.5 @@ -9059,8 +9054,8 @@ __metadata: "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": 0.31.29 "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": 8.0.0 - "@rocket.chat/ui-contexts": 8.0.0 + "@rocket.chat/ui-client": 9.0.0-rc.2 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 katex: "*" react: "*" languageName: unknown @@ -9074,7 +9069,7 @@ __metadata: "@babel/preset-env": ~7.22.20 babel-jest: ^29.5.0 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 tsup: ^6.7.0 typescript: ~5.3.3 @@ -9101,13 +9096,35 @@ __metadata: languageName: unknown linkType: soft +"@rocket.chat/jest-presets@workspace:packages/jest-presets, @rocket.chat/jest-presets@workspace:~": + version: 0.0.0-use.local + resolution: "@rocket.chat/jest-presets@workspace:packages/jest-presets" + dependencies: + "@rocket.chat/eslint-config": "workspace:~" + "@swc/core": ~1.5.24 + "@swc/jest": ~0.2.36 + "@testing-library/jest-dom": ~6.4.5 + "@types/identity-obj-proxy": ^3 + "@types/jest": ~29.5.12 + "@types/jest-axe": ~3.5.9 + "@types/uuid": ^9 + eslint: ~8.45.0 + identity-obj-proxy: ~3.0.0 + jest: ~29.7.0 + jest-axe: ~9.0.0 + jest-environment-jsdom: ~29.7.0 + typescript: ~5.4.5 + uuid: ~9.0.1 + languageName: unknown + linkType: soft + "@rocket.chat/jwt@workspace:^, @rocket.chat/jwt@workspace:packages/jwt": version: 0.0.0-use.local resolution: "@rocket.chat/jwt@workspace:packages/jwt" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 jose: ^4.14.4 ts-jest: ^29.1.1 typescript: ~5.3.3 @@ -9136,12 +9153,12 @@ __metadata: "@swc/core": ^1.3.95 "@swc/jest": ^0.2.29 "@types/bcrypt": ^5.0.1 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/ws": ^8.5.8 bcrypt: ^5.0.1 eslint: ~8.45.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 jest-websocket-mock: ^2.4.0 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -9251,11 +9268,11 @@ __metadata: dependencies: "@types/chalk": ^2.2.0 "@types/ejson": ^2.2.1 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 chalk: ^4.0.0 ejson: ^2.2.3 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -9266,9 +9283,9 @@ __metadata: resolution: "@rocket.chat/logger@workspace:packages/logger" dependencies: "@rocket.chat/emitter": ~0.31.25 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 pino: ^8.15.0 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -9305,12 +9322,12 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/peggy-loader": "workspace:~" "@rocket.chat/prettier-config": ~0.31.25 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/node": ~14.18.42 "@typescript-eslint/parser": ~5.58.0 babel-loader: ~9.1.2 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 npm-run-all: ^4.1.5 peggy: 3.0.2 prettier: ~2.8.7 @@ -9369,16 +9386,17 @@ __metadata: "@rocket.chat/favicon": "workspace:^" "@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2 "@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/fuselage-polyfills": ~0.31.25 - "@rocket.chat/fuselage-toastbar": ^0.32.0 + "@rocket.chat/fuselage-toastbar": ^0.33.0 "@rocket.chat/fuselage-tokens": ^0.33.1 "@rocket.chat/fuselage-ui-kit": "workspace:^" "@rocket.chat/gazzodown": "workspace:^" "@rocket.chat/i18n": "workspace:^" "@rocket.chat/icons": ^0.36.0 "@rocket.chat/instance-status": "workspace:^" + "@rocket.chat/jest-presets": "workspace:~" "@rocket.chat/jwt": "workspace:^" "@rocket.chat/layout": ~0.31.26 "@rocket.chat/license": "workspace:^" @@ -9596,7 +9614,7 @@ __metadata: ip-range-check: ^0.2.0 is-svg: ^4.3.2 isolated-vm: 4.4.2 - jest: ~29.6.4 + jest: ~29.7.0 jschardet: ^3.0.0 jsdom: ^16.7.0 jsdom-global: ^3.0.2 @@ -9717,14 +9735,15 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/mock-providers@workspace:packages/mock-providers" dependencies: + "@rocket.chat/ddp-client": "workspace:~" "@rocket.chat/i18n": "workspace:~" "@rocket.chat/ui-contexts": "workspace:*" "@storybook/react": ~6.5.16 "@tanstack/react-query": ^4.16.1 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 i18next: ~23.4.9 - jest: ~29.6.4 + jest: ~29.7.0 react: ~17.0.2 react-i18next: ~13.2.2 ts-jest: ~29.1.1 @@ -9740,10 +9759,10 @@ __metadata: resolution: "@rocket.chat/model-typings@workspace:packages/model-typings" dependencies: "@rocket.chat/core-typings": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/node-rsa": ^1.1.3 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 mongodb: ^4.17.2 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -9757,9 +9776,9 @@ __metadata: "@rocket.chat/model-typings": "workspace:^" "@swc/core": ^1.3.95 "@swc/jest": ^0.2.29 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ^29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -9789,14 +9808,15 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": ~0.31.25 "@rocket.chat/tools": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/node": ^14.18.63 + date-fns: ^2.28.0 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 - jest: ~29.6.4 + jest: ~29.7.0 mem: ^8.1.1 moment-timezone: ^0.5.43 mongo-message-queue: ^1.0.0 @@ -9822,7 +9842,6 @@ __metadata: "@rocket.chat/omnichannel-services": "workspace:^" "@rocket.chat/pdf-worker": "workspace:^" "@rocket.chat/tools": "workspace:^" - "@rocket.chat/ui-contexts": "workspace:^" "@types/gc-stats": ^1.4.2 "@types/node": ^14.18.63 "@types/polka": ^0.5.6 @@ -9872,10 +9891,10 @@ __metadata: resolution: "@rocket.chat/password-policies@workspace:packages/password-policies" dependencies: "@types/chai": ~4.3.16 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 chai: ^4.3.10 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -9885,9 +9904,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/patch-injection@workspace:packages/patch-injection" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -9905,15 +9924,15 @@ __metadata: "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~13.4.0 "@types/emojione": ^2.2.8 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 "@types/testing-library__jest-dom": ~5.14.9 emoji-assets: ^7.0.1 emoji-toolkit: ^7.0.1 eslint: ~8.45.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 moment: ^2.29.4 moment-timezone: ^0.5.43 react: ^18.2.0 @@ -9948,9 +9967,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/poplib@workspace:packages/node-poplib" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -10004,7 +10023,7 @@ __metadata: "@types/node": ^14.18.63 babel-jest: ^29.0.3 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 mongodb: ^4.17.2 typescript: ~5.3.3 languageName: unknown @@ -10065,8 +10084,8 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.1 "@typescript-eslint/parser": ~5.60.1 eslint: ~8.45.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -10115,12 +10134,12 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/message-parser": "workspace:^" "@rocket.chat/ui-kit": "workspace:~" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 ajv: ^8.11.0 ajv-formats: ^2.1.1 eslint: ~8.45.0 - jest: ~29.6.4 - jest-environment-jsdom: ~29.6.4 + jest: ~29.7.0 + jest-environment-jsdom: ~29.7.0 mongodb: ^4.17.2 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -10145,9 +10164,9 @@ __metadata: resolution: "@rocket.chat/server-cloud-communication@workspace:packages/server-cloud-communication" dependencies: "@rocket.chat/license": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -10157,12 +10176,12 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/server-fetch@workspace:packages/server-fetch" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/proxy-from-env": ^1.0.3 eslint: ~8.45.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.1 - jest: ~29.6.4 + jest: ~29.7.0 node-fetch: 2.3.0 proxy-from-env: ^1.1.0 ts-jest: ~29.1.1 @@ -10180,7 +10199,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.1 "@typescript-eslint/parser": ~5.60.1 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 ts-jest: ~29.1.1 typescript: ~5.3.3 languageName: unknown @@ -10251,9 +10270,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/tools@workspace:packages/tools" dependencies: - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 moment-timezone: ^0.5.43 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -10265,7 +10284,7 @@ __metadata: resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar" dependencies: "@babel/core": ~7.22.20 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/ui-contexts": "workspace:^" "@types/babel__core": ~7.20.3 "@types/react": ~17.0.69 @@ -10279,7 +10298,7 @@ __metadata: typescript: ~5.3.3 peerDependencies: "@rocket.chat/fuselage": "*" - "@rocket.chat/ui-contexts": 8.0.0 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 react: ~17.0.2 languageName: unknown linkType: soft @@ -10291,7 +10310,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/icons": ^0.36.0 "@rocket.chat/mock-providers": "workspace:^" @@ -10311,7 +10330,7 @@ __metadata: "@testing-library/react": ^12.1.5 "@testing-library/react-hooks": ^8.0.1 "@types/babel__core": ~7.20.3 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 eslint: ~8.45.0 @@ -10320,7 +10339,7 @@ __metadata: eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.15 eslint-plugin-testing-library: ~5.11.1 - jest: ~29.6.4 + jest: ~29.7.0 react: ^17.0.2 react-dom: ^17.0.2 react-hook-form: ~7.45.4 @@ -10332,7 +10351,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 8.0.0 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 react: ~17.0.2 languageName: unknown linkType: soft @@ -10344,7 +10363,7 @@ __metadata: "@babel/core": ~7.22.20 "@react-aria/toolbar": ^3.0.0-beta.1 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/icons": ^0.36.0 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -10354,13 +10373,17 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.3 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 + "@types/react": ~17.0.69 + "@types/react-dom": ~17.0.22 eslint: ~8.45.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.15 - jest: ~29.6.4 + jest: ~29.7.0 + react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 + react-dom: ~17.0.2 ts-jest: ~29.1.1 typescript: ~5.3.3 peerDependencies: @@ -10377,18 +10400,19 @@ __metadata: resolution: "@rocket.chat/ui-contexts@workspace:packages/ui-contexts" dependencies: "@rocket.chat/core-typings": "workspace:^" + "@rocket.chat/ddp-client": "workspace:~" "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/i18n": "workspace:~" "@rocket.chat/password-policies": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/react": ~17.0.69 "@types/react-dom": ~17.0.22 "@types/use-sync-external-store": ^0.0.5 eslint: ~8.45.0 eslint-plugin-react-hooks: ^4.6.0 - jest: ~29.6.4 + jest: ~29.7.0 mongodb: ^4.17.2 react: ~17.0.2 ts-jest: ~29.1.1 @@ -10415,10 +10439,10 @@ __metadata: "@babel/plugin-transform-runtime": ~7.21.4 "@babel/preset-env": ~7.21.4 "@rocket.chat/eslint-config": "workspace:~" - "@types/jest": ~29.5.0 + "@types/jest": ~29.5.12 babel-loader: ~9.1.2 eslint: ~8.45.0 - jest: ~29.5.0 + jest: ~29.7.0 npm-run-all: ~4.1.5 prettier: ~2.8.8 rimraf: ~3.0.2 @@ -10436,7 +10460,7 @@ __metadata: resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming" dependencies: "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/icons": ^0.36.0 "@rocket.chat/ui-contexts": "workspace:~" @@ -10450,14 +10474,14 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/react": ~17.0.69 eslint: ~8.45.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-testing-library: ^5.11.1 - jest: ~29.6.4 + jest: ~29.7.0 react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.1.1 @@ -10479,12 +10503,14 @@ __metadata: "@rocket.chat/css-in-js": ~0.31.25 "@rocket.chat/emitter": ~0.31.25 "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/icons": ^0.36.0 + "@rocket.chat/jest-presets": "workspace:~" "@rocket.chat/styled": ~0.31.25 "@rocket.chat/ui-avatar": "workspace:^" "@rocket.chat/ui-contexts": "workspace:^" + "@storybook/addon-a11y": ^6.5.16 "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 "@storybook/addon-essentials": ~6.5.16 @@ -10492,13 +10518,16 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 + "@storybook/testing-react": ~1.3.0 "@types/babel__core": ~7.20.3 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 + "@types/jest-axe": ~3.5.9 eslint: ~8.45.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.15 - jest: ~29.6.4 + jest: ~29.7.0 + jest-axe: ~9.0.0 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.1.1 typescript: ~5.3.3 @@ -10508,8 +10537,8 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-avatar": 4.0.0 - "@rocket.chat/ui-contexts": 8.0.0 + "@rocket.chat/ui-avatar": 5.0.0-rc.2 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -10524,10 +10553,10 @@ __metadata: "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 "@rocket.chat/css-in-js": ~0.31.25 - "@rocket.chat/fuselage": ^0.55.2 + "@rocket.chat/fuselage": ^0.57.0 "@rocket.chat/fuselage-hooks": ^0.33.1 "@rocket.chat/fuselage-polyfills": ~0.31.25 - "@rocket.chat/fuselage-toastbar": ^0.32.0 + "@rocket.chat/fuselage-toastbar": ^0.33.0 "@rocket.chat/fuselage-tokens": ^0.33.1 "@rocket.chat/fuselage-ui-kit": "workspace:~" "@rocket.chat/icons": ^0.36.0 @@ -10585,11 +10614,11 @@ __metadata: "@storybook/testing-library": ^0.2.2 "@tanstack/react-query": ^4.16.1 "@testing-library/react": ^13.3.0 - "@types/jest": ~29.5.7 + "@types/jest": ~29.5.12 "@types/react": ~17.0.69 babel-loader: ~8.3.0 eslint: ~8.45.0 - jest: ~29.6.4 + jest: ~29.7.0 react: ~17.0.2 react-hook-form: ~7.45.4 react-i18next: ~13.2.2 @@ -10598,8 +10627,8 @@ __metadata: typescript: ~5.3.3 peerDependencies: "@rocket.chat/layout": "*" - "@rocket.chat/tools": 0.2.1 - "@rocket.chat/ui-contexts": 8.0.0 + "@rocket.chat/tools": 0.2.2-rc.0 + "@rocket.chat/ui-contexts": 9.0.0-rc.2 "@tanstack/react-query": "*" react: "*" react-hook-form: "*" @@ -10858,7 +10887,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-a11y@npm:6.5.16": +"@storybook/addon-a11y@npm:6.5.16, @storybook/addon-a11y@npm:^6.5.16": version: 6.5.16 resolution: "@storybook/addon-a11y@npm:6.5.16" dependencies: @@ -11835,6 +11864,15 @@ __metadata: languageName: node linkType: hard +"@storybook/csf@npm:0.0.2--canary.87bc651.0": + version: 0.0.2--canary.87bc651.0 + resolution: "@storybook/csf@npm:0.0.2--canary.87bc651.0" + dependencies: + lodash: ^4.17.15 + checksum: 1533ff81f7fb59c06fc608f452de3cfcafba5806da68dd2c88813e8284a7aa1c158daee6a58b028b7ccd03d96974b5d3727deaae1d1d38e304b2a7cdcd8a678d + languageName: node + linkType: hard + "@storybook/csf@npm:^0.0.1": version: 0.0.1 resolution: "@storybook/csf@npm:0.0.1" @@ -12374,6 +12412,21 @@ __metadata: languageName: node linkType: hard +"@storybook/testing-react@npm:~1.3.0": + version: 1.3.0 + resolution: "@storybook/testing-react@npm:1.3.0" + dependencies: + "@storybook/csf": 0.0.2--canary.87bc651.0 + peerDependencies: + "@storybook/addons": ">=6.4.0" + "@storybook/client-api": ">=6.4.0" + "@storybook/preview-web": ">=6.4.0" + "@storybook/react": ">=6.4.0" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: f7da948bfc100b7a3d8f3ddf02e8d610da80344904761ecdaa7a575dcc312c921ef4a28663edcc719aee587b14716643147b0e10414fa17abb740056cf9e80b5 + languageName: node + linkType: hard + "@storybook/theming@npm:6.5.16, @storybook/theming@npm:~6.5.16": version: 6.5.16 resolution: "@storybook/theming@npm:6.5.16" @@ -12441,94 +12494,94 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-darwin-arm64@npm:1.3.95" +"@swc/core-darwin-arm64@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-darwin-arm64@npm:1.5.29" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-darwin-x64@npm:1.3.95" +"@swc/core-darwin-x64@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-darwin-x64@npm:1.5.29" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.95" +"@swc/core-linux-arm-gnueabihf@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.29" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.95" +"@swc/core-linux-arm64-gnu@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-linux-arm64-gnu@npm:1.5.29" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.95" +"@swc/core-linux-arm64-musl@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-linux-arm64-musl@npm:1.5.29" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.95" +"@swc/core-linux-x64-gnu@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-linux-x64-gnu@npm:1.5.29" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-linux-x64-musl@npm:1.3.95" +"@swc/core-linux-x64-musl@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-linux-x64-musl@npm:1.5.29" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.95" +"@swc/core-win32-arm64-msvc@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-win32-arm64-msvc@npm:1.5.29" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.95" +"@swc/core-win32-ia32-msvc@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-win32-ia32-msvc@npm:1.5.29" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.95": - version: 1.3.95 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.95" +"@swc/core-win32-x64-msvc@npm:1.5.29": + version: 1.5.29 + resolution: "@swc/core-win32-x64-msvc@npm:1.5.29" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:^1.3.95": - version: 1.3.95 - resolution: "@swc/core@npm:1.3.95" +"@swc/core@npm:^1.3.95, @swc/core@npm:~1.5.24": + version: 1.5.29 + resolution: "@swc/core@npm:1.5.29" dependencies: - "@swc/core-darwin-arm64": 1.3.95 - "@swc/core-darwin-x64": 1.3.95 - "@swc/core-linux-arm-gnueabihf": 1.3.95 - "@swc/core-linux-arm64-gnu": 1.3.95 - "@swc/core-linux-arm64-musl": 1.3.95 - "@swc/core-linux-x64-gnu": 1.3.95 - "@swc/core-linux-x64-musl": 1.3.95 - "@swc/core-win32-arm64-msvc": 1.3.95 - "@swc/core-win32-ia32-msvc": 1.3.95 - "@swc/core-win32-x64-msvc": 1.3.95 - "@swc/counter": ^0.1.1 - "@swc/types": ^0.1.5 + "@swc/core-darwin-arm64": 1.5.29 + "@swc/core-darwin-x64": 1.5.29 + "@swc/core-linux-arm-gnueabihf": 1.5.29 + "@swc/core-linux-arm64-gnu": 1.5.29 + "@swc/core-linux-arm64-musl": 1.5.29 + "@swc/core-linux-x64-gnu": 1.5.29 + "@swc/core-linux-x64-musl": 1.5.29 + "@swc/core-win32-arm64-msvc": 1.5.29 + "@swc/core-win32-ia32-msvc": 1.5.29 + "@swc/core-win32-x64-msvc": 1.5.29 + "@swc/counter": ^0.1.3 + "@swc/types": ^0.1.8 peerDependencies: - "@swc/helpers": ^0.5.0 + "@swc/helpers": "*" dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -12553,14 +12606,14 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 49856ad64fa16151f7f784956c8134060c5ed612c9b3fcb79e33676c77219f61fa3bb6b9e0dd15d78c7bff92cacc0c944d2b4e60849eb93caca917a8544b1c2c + checksum: 19e3f8525ce4f4d9f6f3f62825b1f13e1e76146390930b6d5e663c0bb2c5e95ef3bf30268110d1444ea92b99dd95bfe5a3c6e2a3c0dd79291505dc7da1ed3dab languageName: node linkType: hard -"@swc/counter@npm:^0.1.1": - version: 0.1.2 - resolution: "@swc/counter@npm:0.1.2" - checksum: 8427c594f1f0cf44b83885e9c8fe1e370c9db44ae96e07a37c117a6260ee97797d0709483efbcc244e77bac578690215f45b23254c4cd8a70fb25ddbb50bf33e +"@swc/counter@npm:^0.1.3": + version: 0.1.3 + resolution: "@swc/counter@npm:0.1.3" + checksum: df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598 languageName: node linkType: hard @@ -12582,22 +12635,25 @@ __metadata: languageName: node linkType: hard -"@swc/jest@npm:^0.2.29": - version: 0.2.29 - resolution: "@swc/jest@npm:0.2.29" +"@swc/jest@npm:^0.2.29, @swc/jest@npm:~0.2.36": + version: 0.2.36 + resolution: "@swc/jest@npm:0.2.36" dependencies: - "@jest/create-cache-key-function": ^27.4.2 + "@jest/create-cache-key-function": ^29.7.0 + "@swc/counter": ^0.1.3 jsonc-parser: ^3.2.0 peerDependencies: "@swc/core": "*" - checksum: 9eaad322310f34e81f67d41411a7d60663341af1bd9fb65456faa914c936d849d6f643fa3b942a187d52e71e62c33097098c639d25c2047fa874f49bf51cec76 + checksum: 14f2e696ac093e23dae1e2e57d894bbcde4de6fe80341a26c8d0d8cbae5aae31832f8fa32dc698529f128d19a76aeedf2227f59480de6dab5eb3f30bfdf9b71a languageName: node linkType: hard -"@swc/types@npm:^0.1.5": - version: 0.1.5 - resolution: "@swc/types@npm:0.1.5" - checksum: 6aee11f62d3d805a64848e0bd5f0e0e615f958e327a9e1260056c368d7d28764d89e38bd8005a536c9bf18afbcd303edd84099d60df34a2975d62540f61df13b +"@swc/types@npm:^0.1.8": + version: 0.1.9 + resolution: "@swc/types@npm:0.1.9" + dependencies: + "@swc/counter": ^0.1.3 + checksum: 16fcdf331c94c52f6dbf234bd9c294a5479e4b107eb8bce364b46cd5cf86ff7073e371a3a02573e6843b0ec979e6d8912b6f60212ad297aa170a9025e7ebb716 languageName: node linkType: hard @@ -12700,6 +12756,39 @@ __metadata: languageName: node linkType: hard +"@testing-library/jest-dom@npm:~6.4.5": + version: 6.4.6 + resolution: "@testing-library/jest-dom@npm:6.4.6" + dependencies: + "@adobe/css-tools": ^4.4.0 + "@babel/runtime": ^7.9.2 + aria-query: ^5.0.0 + chalk: ^3.0.0 + css.escape: ^1.5.1 + dom-accessibility-api: ^0.6.3 + lodash: ^4.17.21 + redent: ^3.0.0 + peerDependencies: + "@jest/globals": ">= 28" + "@types/bun": "*" + "@types/jest": ">= 28" + jest: ">= 28" + vitest: ">= 0.32" + peerDependenciesMeta: + "@jest/globals": + optional: true + "@types/bun": + optional: true + "@types/jest": + optional: true + jest: + optional: true + vitest: + optional: true + checksum: d70acbfc5d842065292dc1b4113ac2b4c2a2b83f9868e454d7f24d97ee92fddf7852e0e079b6eecaf21154bfe6e9ad03eb32e72f16854f64d7ce1ff42288828b + languageName: node + linkType: hard + "@testing-library/react-hooks@npm:^8.0.1": version: 8.0.1 resolution: "@testing-library/react-hooks@npm:8.0.1" @@ -12761,16 +12850,7 @@ __metadata: languageName: node linkType: hard -"@testing-library/user-event@npm:^14.4.0": - version: 14.5.1 - resolution: "@testing-library/user-event@npm:14.5.1" - peerDependencies: - "@testing-library/dom": ">=7.21.4" - checksum: 3e6bc9fd53dfe2f3648190193ed2fd4bca2a1bfb47f68810df3b33f05412526e5fd5c4ef9dc5375635e0f4cdf1859916867b597eed22bda1321e04242ea6c519 - languageName: node - linkType: hard - -"@testing-library/user-event@npm:^14.5.2": +"@testing-library/user-event@npm:^14.4.0, @testing-library/user-event@npm:^14.5.2": version: 14.5.2 resolution: "@testing-library/user-event@npm:14.5.2" peerDependencies: @@ -13660,6 +13740,13 @@ __metadata: languageName: node linkType: hard +"@types/identity-obj-proxy@npm:^3": + version: 3.0.2 + resolution: "@types/identity-obj-proxy@npm:3.0.2" + checksum: 77387ee587657ab24f12a1dee5c0e1386358d5c38cda5cac78bc5049340cb358e009f6254de3bbdee6a08e46f13b1552cd47a0bbd3e7a53ff469bf58a04ec6e9 + languageName: node + linkType: hard + "@types/imap@npm:^0.8.39": version: 0.8.39 resolution: "@types/imap@npm:0.8.39" @@ -13719,7 +13806,17 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:^29.5.12, @types/jest@npm:~29.5.0, @types/jest@npm:~29.5.7": +"@types/jest-axe@npm:~3.5.9": + version: 3.5.9 + resolution: "@types/jest-axe@npm:3.5.9" + dependencies: + "@types/jest": "*" + axe-core: ^3.5.5 + checksum: ca93cf7b13f173278f8c67e4f7d73c9c0cf3c6110f5d76a8815e1044c1b2950fe4b4e83475f6d23bd82479edc908da19e037cf8e1a9c0e89f1c8f5b95355c93d + languageName: node + linkType: hard + +"@types/jest@npm:*, @types/jest@npm:~29.5.12": version: 29.5.12 resolution: "@types/jest@npm:29.5.12" dependencies: @@ -14623,6 +14720,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^9": + version: 9.0.8 + resolution: "@types/uuid@npm:9.0.8" + checksum: b8c60b7ba8250356b5088302583d1704a4e1a13558d143c549c408bf8920535602ffc12394ede77f8a8083511b023704bc66d1345792714002bfa261b17c5275 + languageName: node + linkType: hard + "@types/webidl-conversions@npm:*": version: 6.1.1 resolution: "@types/webidl-conversions@npm:6.1.1" @@ -16241,7 +16345,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:5.1.3, aria-query@npm:^5.0.0": +"aria-query@npm:5.1.3": version: 5.1.3 resolution: "aria-query@npm:5.1.3" dependencies: @@ -16250,7 +16354,7 @@ __metadata: languageName: node linkType: hard -"aria-query@npm:^5.3.0": +"aria-query@npm:^5.0.0, aria-query@npm:^5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" dependencies: @@ -16280,17 +16384,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.0": - version: 1.0.0 - resolution: "array-buffer-byte-length@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - is-array-buffer: ^3.0.1 - checksum: 044e101ce150f4804ad19c51d6c4d4cfa505c5b2577bd179256e4aa3f3f6a0a5e9874c78cd428ee566ac574c8a04d7ce21af9fe52e844abfdccb82b33035a7c3 - languageName: node - linkType: hard - -"array-buffer-byte-length@npm:^1.0.1": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "array-buffer-byte-length@npm:1.0.1" dependencies: @@ -16321,20 +16415,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.0.3, array-includes@npm:^3.1.4, array-includes@npm:^3.1.6": - version: 3.1.6 - resolution: "array-includes@npm:3.1.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - get-intrinsic: ^1.1.3 - is-string: ^1.0.7 - checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 - languageName: node - linkType: hard - -"array-includes@npm:^3.1.7": +"array-includes@npm:^3.0.3, array-includes@npm:^3.1.4, array-includes@npm:^3.1.6, array-includes@npm:^3.1.7": version: 3.1.8 resolution: "array-includes@npm:3.1.8" dependencies: @@ -16392,19 +16473,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.flat@npm:^1.2.1, array.prototype.flat@npm:^1.2.3, array.prototype.flat@npm:^1.2.5": - version: 1.3.1 - resolution: "array.prototype.flat@npm:1.3.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - es-shim-unscopables: ^1.0.0 - checksum: 5a8415949df79bf6e01afd7e8839bbde5a3581300e8ad5d8449dea52639e9e59b26a467665622783697917b43bf39940a6e621877c7dd9b3d1c1f97484b9b88b - languageName: node - linkType: hard - -"array.prototype.flat@npm:^1.3.1": +"array.prototype.flat@npm:^1.2.1, array.prototype.flat@npm:^1.2.3, array.prototype.flat@npm:^1.2.5, array.prototype.flat@npm:^1.3.1": version: 1.3.2 resolution: "array.prototype.flat@npm:1.3.2" dependencies: @@ -16416,19 +16485,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.2.1, array.prototype.flatmap@npm:^1.3.1": - version: 1.3.1 - resolution: "array.prototype.flatmap@npm:1.3.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - es-shim-unscopables: ^1.0.0 - checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 - languageName: node - linkType: hard - -"array.prototype.flatmap@npm:^1.3.2": +"array.prototype.flatmap@npm:^1.2.1, array.prototype.flatmap@npm:^1.3.1, array.prototype.flatmap@npm:^1.3.2": version: 1.3.2 resolution: "array.prototype.flatmap@npm:1.3.2" dependencies: @@ -16466,21 +16523,6 @@ __metadata: languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.2": - version: 1.0.2 - resolution: "arraybuffer.prototype.slice@npm:1.0.2" - dependencies: - array-buffer-byte-length: ^1.0.0 - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - get-intrinsic: ^1.2.1 - is-array-buffer: ^3.0.2 - is-shared-array-buffer: ^1.0.2 - checksum: c200faf437786f5b2c80d4564ff5481c886a16dee642ef02abdc7306c7edd523d1f01d1dd12b769c7eb42ac9bc53874510db19a92a2c035c0f6696172aafa5d3 - languageName: node - linkType: hard - "arraybuffer.prototype.slice@npm:^1.0.3": version: 1.0.3 resolution: "arraybuffer.prototype.slice@npm:1.0.3" @@ -16747,13 +16789,6 @@ __metadata: languageName: node linkType: hard -"available-typed-arrays@npm:^1.0.5": - version: 1.0.5 - resolution: "available-typed-arrays@npm:1.0.5" - checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a - languageName: node - linkType: hard - "available-typed-arrays@npm:^1.0.7": version: 1.0.7 resolution: "available-typed-arrays@npm:1.0.7" @@ -16810,6 +16845,13 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:4.9.1": + version: 4.9.1 + resolution: "axe-core@npm:4.9.1" + checksum: 41d9227871781f96c2952e2a777fca73624959dd0e98864f6d82806a77602f82b4fc490852082a7e524d8cd864e50d8b4d9931819b4a150112981d8c932110c5 + languageName: node + linkType: hard + "axe-core@npm:=4.7.0": version: 4.7.0 resolution: "axe-core@npm:4.7.0" @@ -16817,6 +16859,13 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:^3.5.5": + version: 3.5.6 + resolution: "axe-core@npm:3.5.6" + checksum: 000777d2b6bf1f390beb1fb4b8714ed9127797c021c345b032db0c144e07320dbbe8cb0bcb7688b90b79cfbd3cdc1f27a4dc857804e3c61d7e0defb34deeb830 + languageName: node + linkType: hard + "axe-core@npm:^4.2.0, axe-core@npm:^4.7.0": version: 4.7.2 resolution: "axe-core@npm:4.7.2" @@ -18186,17 +18235,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" - dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 - languageName: node - linkType: hard - -"call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" dependencies: @@ -18505,6 +18544,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:4.1.2, chalk@npm:^4, chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + "chalk@npm:^1.0.0": version: 1.1.3 resolution: "chalk@npm:1.1.3" @@ -18518,16 +18567,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4, chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc - languageName: node - linkType: hard - "change-case@npm:^4.1.2": version: 4.1.2 resolution: "change-case@npm:4.1.2" @@ -20977,18 +21016,7 @@ __metadata: languageName: node linkType: hard -"define-data-property@npm:^1.0.1": - version: 1.1.0 - resolution: "define-data-property@npm:1.1.0" - dependencies: - get-intrinsic: ^1.2.1 - gopd: ^1.0.1 - has-property-descriptors: ^1.0.0 - checksum: 7ad4ee84cca8ad427a4831f5693526804b62ce9dfd4efac77214e95a4382aed930072251d4075dc8dc9fc949a353ed51f19f5285a84a788ba9216cc51472a093 - languageName: node - linkType: hard - -"define-data-property@npm:^1.1.4": +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" dependencies: @@ -21006,17 +21034,7 @@ __metadata: languageName: node linkType: hard -"define-properties@npm:^1.1.2, define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" - dependencies: - has-property-descriptors: ^1.0.0 - object-keys: ^1.1.1 - checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 - languageName: node - linkType: hard - -"define-properties@npm:^1.2.1": +"define-properties@npm:^1.1.2, define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" dependencies: @@ -21368,6 +21386,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.6.3": + version: 0.6.3 + resolution: "dom-accessibility-api@npm:0.6.3" + checksum: c325b5144bb406df23f4affecffc117dbaec9af03daad9ee6b510c5be647b14d28ef0a4ea5ca06d696d8ab40bb777e5fed98b985976fdef9d8790178fa1d573f + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -22043,54 +22068,7 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.17.2, es-abstract@npm:^1.19.1, es-abstract@npm:^1.20.4, es-abstract@npm:^1.22.1": - version: 1.22.2 - resolution: "es-abstract@npm:1.22.2" - dependencies: - array-buffer-byte-length: ^1.0.0 - arraybuffer.prototype.slice: ^1.0.2 - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - es-set-tostringtag: ^2.0.1 - es-to-primitive: ^1.2.1 - function.prototype.name: ^1.1.6 - get-intrinsic: ^1.2.1 - get-symbol-description: ^1.0.0 - globalthis: ^1.0.3 - gopd: ^1.0.1 - has: ^1.0.3 - has-property-descriptors: ^1.0.0 - has-proto: ^1.0.1 - has-symbols: ^1.0.3 - internal-slot: ^1.0.5 - is-array-buffer: ^3.0.2 - is-callable: ^1.2.7 - is-negative-zero: ^2.0.2 - is-regex: ^1.1.4 - is-shared-array-buffer: ^1.0.2 - is-string: ^1.0.7 - is-typed-array: ^1.1.12 - is-weakref: ^1.0.2 - object-inspect: ^1.12.3 - object-keys: ^1.1.1 - object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.1 - safe-array-concat: ^1.0.1 - safe-regex-test: ^1.0.0 - string.prototype.trim: ^1.2.8 - string.prototype.trimend: ^1.0.7 - string.prototype.trimstart: ^1.0.7 - typed-array-buffer: ^1.0.0 - typed-array-byte-length: ^1.0.0 - typed-array-byte-offset: ^1.0.0 - typed-array-length: ^1.0.4 - unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.11 - checksum: cc70e592d360d7d729859013dee7a610c6b27ed8630df0547c16b0d16d9fe6505a70ee14d1af08d970fdd132b3f88c9ca7815ce72c9011608abf8ab0e55fc515 - languageName: node - linkType: hard - -"es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2": +"es-abstract@npm:^1.17.2, es-abstract@npm:^1.19.1, es-abstract@npm:^1.20.4, es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0, es-abstract@npm:^1.23.1, es-abstract@npm:^1.23.2": version: 1.23.3 resolution: "es-abstract@npm:1.23.3" dependencies: @@ -22229,17 +22207,6 @@ __metadata: languageName: node linkType: hard -"es-set-tostringtag@npm:^2.0.1": - version: 2.0.1 - resolution: "es-set-tostringtag@npm:2.0.1" - dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 - has-tostringtag: ^1.0.0 - checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 - languageName: node - linkType: hard - "es-set-tostringtag@npm:^2.0.3": version: 2.0.3 resolution: "es-set-tostringtag@npm:2.0.3" @@ -24106,17 +24073,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.2": - version: 1.15.2 - resolution: "follow-redirects@npm:1.15.2" - peerDependenciesMeta: - debug: - optional: true - checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.10.0": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.10.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.2": version: 1.15.4 resolution: "follow-redirects@npm:1.15.4" peerDependenciesMeta: @@ -24671,19 +24628,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": - version: 1.2.1 - resolution: "get-intrinsic@npm:1.2.1" - dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 - has-proto: ^1.0.1 - has-symbols: ^1.0.3 - checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" dependencies: @@ -24775,17 +24720,7 @@ __metadata: languageName: node linkType: hard -"get-symbol-description@npm:^1.0.0": - version: 1.0.0 - resolution: "get-symbol-description@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.1 - checksum: 9ceff8fe968f9270a37a1f73bf3f1f7bda69ca80f4f80850670e0e7b9444ff99323f7ac52f96567f8b5f5fbe7ac717a0d81d3407c7313e82810c6199446a5247 - languageName: node - linkType: hard - -"get-symbol-description@npm:^1.0.2": +"get-symbol-description@npm:^1.0.0, get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" dependencies: @@ -25448,16 +25383,7 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" - dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb - languageName: node - linkType: hard - -"has-property-descriptors@npm:^1.0.2": +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": version: 1.0.2 resolution: "has-property-descriptors@npm:1.0.2" dependencies: @@ -25466,14 +25392,7 @@ __metadata: languageName: node linkType: hard -"has-proto@npm:^1.0.1": - version: 1.0.1 - resolution: "has-proto@npm:1.0.1" - checksum: febc5b5b531de8022806ad7407935e2135f1cc9e64636c3916c6842bd7995994ca3b29871ecd7954bd35f9e2986c17b3b227880484d22259e2f8e6ce63fd383e - languageName: node - linkType: hard - -"has-proto@npm:^1.0.3": +"has-proto@npm:^1.0.1, has-proto@npm:^1.0.3": version: 1.0.3 resolution: "has-proto@npm:1.0.3" checksum: fe7c3d50b33f50f3933a04413ed1f69441d21d2d2944f81036276d30635cad9279f6b43bc8f32036c31ebdfcf6e731150f46c1907ad90c669ffe9b066c3ba5c4 @@ -25503,16 +25422,7 @@ __metadata: languageName: node linkType: hard -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" - dependencies: - has-symbols: ^1.0.2 - checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.2": +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.2": version: 1.0.2 resolution: "has-tostringtag@npm:1.0.2" dependencies: @@ -25607,16 +25517,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0": - version: 2.0.0 - resolution: "hasown@npm:2.0.0" - dependencies: - function-bind: ^1.1.2 - checksum: 6151c75ca12554565098641c98a40f4cc86b85b0fd5b6fe92360967e4605a4f9610f7757260b4e8098dd1c2ce7f4b095f2006fe72a570e3b6d2d28de0298c176 - languageName: node - linkType: hard - -"hasown@npm:^2.0.1, hasown@npm:^2.0.2": +"hasown@npm:^2.0.0, hasown@npm:^2.0.1, hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -26306,16 +26207,7 @@ __metadata: languageName: node linkType: hard -"i18next@npm:>=17.0.11, i18next@npm:~23.4.9": - version: 23.4.9 - resolution: "i18next@npm:23.4.9" - dependencies: - "@babel/runtime": ^7.22.5 - checksum: 46ac2415dc817c0e25e1c5e64332249825cd63b5fb9a5d42e3a2329d4ecd098c0f15a243f56b53e03db02e2e9931ca9bb4447370b2436f9ad6556de40ce981cf - languageName: node - linkType: hard - -"i18next@npm:^23.10.1": +"i18next@npm:>=17.0.11, i18next@npm:^23.10.1": version: 23.10.1 resolution: "i18next@npm:23.10.1" dependencies: @@ -26333,6 +26225,15 @@ __metadata: languageName: node linkType: hard +"i18next@npm:~23.4.9": + version: 23.4.9 + resolution: "i18next@npm:23.4.9" + dependencies: + "@babel/runtime": ^7.22.5 + checksum: 46ac2415dc817c0e25e1c5e64332249825cd63b5fb9a5d42e3a2329d4ecd098c0f15a243f56b53e03db02e2e9931ca9bb4447370b2436f9ad6556de40ce981cf + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24, iconv-lite@npm:^0.4.4": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -26378,7 +26279,7 @@ __metadata: languageName: node linkType: hard -"identity-obj-proxy@npm:^3.0.0": +"identity-obj-proxy@npm:^3.0.0, identity-obj-proxy@npm:~3.0.0": version: 3.0.0 resolution: "identity-obj-proxy@npm:3.0.0" dependencies: @@ -26801,18 +26702,7 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5": - version: 1.0.5 - resolution: "internal-slot@npm:1.0.5" - dependencies: - get-intrinsic: ^1.2.0 - has: ^1.0.3 - side-channel: ^1.0.4 - checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a - languageName: node - linkType: hard - -"internal-slot@npm:^1.0.7": +"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" dependencies: @@ -26998,18 +26888,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": - version: 3.0.2 - resolution: "is-array-buffer@npm:3.0.2" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.2.0 - is-typed-array: ^1.1.10 - checksum: dcac9dda66ff17df9cabdc58214172bf41082f956eab30bb0d86bc0fab1e44b690fc8e1f855cf2481245caf4e8a5a006a982a71ddccec84032ed41f9d8da8c14 - languageName: node - linkType: hard - -"is-array-buffer@npm:^3.0.4": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": version: 3.0.4 resolution: "is-array-buffer@npm:3.0.4" dependencies: @@ -27464,13 +27343,6 @@ __metadata: languageName: node linkType: hard -"is-negative-zero@npm:^2.0.2": - version: 2.0.2 - resolution: "is-negative-zero@npm:2.0.2" - checksum: f3232194c47a549da60c3d509c9a09be442507616b69454716692e37ae9f37c4dea264fb208ad0c9f3efd15a796a46b79df07c7e53c6227c32170608b809149a - languageName: node - linkType: hard - "is-negative-zero@npm:^2.0.3": version: 2.0.3 resolution: "is-negative-zero@npm:2.0.3" @@ -27657,16 +27529,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.2": - version: 1.0.2 - resolution: "is-shared-array-buffer@npm:1.0.2" - dependencies: - call-bind: ^1.0.2 - checksum: 9508929cf14fdc1afc9d61d723c6e8d34f5e117f0bffda4d97e7a5d88c3a8681f633a74f8e3ad1fe92d5113f9b921dc5ca44356492079612f9a247efbce7032a - languageName: node - linkType: hard - -"is-shared-array-buffer@npm:^1.0.3": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.3": version: 1.0.3 resolution: "is-shared-array-buffer@npm:1.0.3" dependencies: @@ -27725,16 +27588,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": - version: 1.1.12 - resolution: "is-typed-array@npm:1.1.12" - dependencies: - which-typed-array: ^1.1.11 - checksum: 4c89c4a3be07186caddadf92197b17fda663a9d259ea0d44a85f171558270d36059d1c386d34a12cba22dfade5aba497ce22778e866adc9406098c8fc4771796 - languageName: node - linkType: hard - -"is-typed-array@npm:^1.1.13": +"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.3": version: 1.1.13 resolution: "is-typed-array@npm:1.1.13" dependencies: @@ -28092,6 +27946,18 @@ __metadata: languageName: node linkType: hard +"jest-axe@npm:~9.0.0": + version: 9.0.0 + resolution: "jest-axe@npm:9.0.0" + dependencies: + axe-core: 4.9.1 + chalk: 4.1.2 + jest-matcher-utils: 29.2.2 + lodash.merge: 4.6.2 + checksum: 04457ed3d03e0b7745584325eab446da65d2093f6b608a83955d5ff0ec6a4e026db0da77c755b5e4d8d582ece443d2921da3d94d52eb499f51c105a1cc784cc7 + languageName: node + linkType: hard + "jest-changed-files@npm:^29.7.0": version: 29.7.0 resolution: "jest-changed-files@npm:29.7.0" @@ -28131,7 +27997,7 @@ __metadata: languageName: node linkType: hard -"jest-cli@npm:^29.5.0, jest-cli@npm:^29.6.4, jest-cli@npm:^29.7.0": +"jest-cli@npm:^29.7.0": version: 29.7.0 resolution: "jest-cli@npm:29.7.0" dependencies: @@ -28207,7 +28073,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.2.1, jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -28241,24 +28107,24 @@ __metadata: languageName: node linkType: hard -"jest-environment-jsdom@npm:~29.6.4": - version: 29.6.4 - resolution: "jest-environment-jsdom@npm:29.6.4" +"jest-environment-jsdom@npm:~29.7.0": + version: 29.7.0 + resolution: "jest-environment-jsdom@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.4 - "@jest/fake-timers": ^29.6.4 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 "@jest/types": ^29.6.3 "@types/jsdom": ^20.0.0 "@types/node": "*" - jest-mock: ^29.6.3 - jest-util: ^29.6.3 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 jsdom: ^20.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 2afe105f12d7d93ca56e2e6f67ab07ada3dd3da0516d1198f254930683ab9feb2b8c14417baaca53544eed88fd7fb5744f0dbce2e100269746187317ce0347df + checksum: 559aac134c196fccc1dfc794d8fc87377e9f78e894bb13012b0831d88dec0abd7ece99abec69da564b8073803be4f04a9eb4f4d1bb80e29eec0cb252c254deb8 languageName: node linkType: hard @@ -28293,7 +28159,7 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^29.6.3": +"jest-get-type@npm:^29.2.0, jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 @@ -28358,6 +28224,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:29.2.2": + version: 29.2.2 + resolution: "jest-matcher-utils@npm:29.2.2" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.2.1 + jest-get-type: ^29.2.0 + pretty-format: ^29.2.1 + checksum: 97ef2638ab826c25f84bfedea231cef091820ae0876ba316922da81145e950d2b9d2057d3645813b5ee880bb975ed4f22e228dda5d0d26a20715e575b675357d + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.7.0": version: 29.7.0 resolution: "jest-matcher-utils@npm:29.7.0" @@ -28397,7 +28275,7 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.6.3, jest-mock@npm:^29.7.0": +"jest-mock@npm:^29.7.0": version: 29.7.0 resolution: "jest-mock@npm:29.7.0" dependencies: @@ -28572,7 +28450,7 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.0.0, jest-util@npm:^29.6.3, jest-util@npm:^29.7.0": +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" dependencies: @@ -28660,7 +28538,7 @@ __metadata: languageName: node linkType: hard -"jest@npm:^29.7.0": +"jest@npm:^29.7.0, jest@npm:~29.7.0": version: 29.7.0 resolution: "jest@npm:29.7.0" dependencies: @@ -28679,44 +28557,6 @@ __metadata: languageName: node linkType: hard -"jest@npm:~29.5.0": - version: 29.5.0 - resolution: "jest@npm:29.5.0" - dependencies: - "@jest/core": ^29.5.0 - "@jest/types": ^29.5.0 - import-local: ^3.0.2 - jest-cli: ^29.5.0 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: a8ff2eb0f421623412236e23cbe67c638127fffde466cba9606bc0c0553b4c1e5cb116d7e0ef990b5d1712851652c8ee461373b578df50857fe635b94ff455d5 - languageName: node - linkType: hard - -"jest@npm:~29.6.4": - version: 29.6.4 - resolution: "jest@npm:29.6.4" - dependencies: - "@jest/core": ^29.6.4 - "@jest/types": ^29.6.3 - import-local: ^3.0.2 - jest-cli: ^29.6.4 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: ba28ca7a86d029bcd742bb254c0c8d0119c1e002ddae128ff6409ebabc0b29c36f69dbf3fdd326aff16e7b2500c9a918bbc6a9a5db4d966e035127242239439f - languageName: node - linkType: hard - "jmespath@npm:0.16.0": version: 0.16.0 resolution: "jmespath@npm:0.16.0" @@ -29154,17 +28994,7 @@ __metadata: languageName: node linkType: hard -"jsx-ast-utils@npm:^2.4.1 || ^3.0.0": - version: 3.2.2 - resolution: "jsx-ast-utils@npm:3.2.2" - dependencies: - array-includes: ^3.1.4 - object.assign: ^4.1.2 - checksum: 88c7ade9e1edb8e27021c9ac194184f47d6ffd3852807c3aac44b1610f7eb33359e1aa872a35008d43ed66b5f7be0f6fd8d6e0574d01cf3a4af3ceb0cd0b5988 - languageName: node - linkType: hard - -"jsx-ast-utils@npm:^3.3.5": +"jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5": version: 3.3.5 resolution: "jsx-ast-utils@npm:3.3.5" dependencies: @@ -29848,7 +29678,7 @@ __metadata: languageName: node linkType: hard -"lodash.merge@npm:^4.6.2": +"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 @@ -32404,14 +32234,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": - version: 1.12.3 - resolution: "object-inspect@npm:1.12.3" - checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db - languageName: node - linkType: hard - -"object-inspect@npm:^1.13.1": +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": version: 1.13.1 resolution: "object-inspect@npm:1.13.1" checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f @@ -32451,19 +32274,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.2, object.assign@npm:^4.1.4": - version: 4.1.4 - resolution: "object.assign@npm:4.1.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - has-symbols: ^1.0.3 - object-keys: ^1.1.1 - checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 - languageName: node - linkType: hard - -"object.assign@npm:^4.1.5": +"object.assign@npm:^4.1.4, object.assign@npm:^4.1.5": version: 4.1.5 resolution: "object.assign@npm:4.1.5" dependencies: @@ -32475,18 +32286,7 @@ __metadata: languageName: node linkType: hard -"object.entries@npm:^1.1.0, object.entries@npm:^1.1.6": - version: 1.1.6 - resolution: "object.entries@npm:1.1.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 0f8c47517e6a9a980241eafe3b73de11e59511883173c2b93d67424a008e47e11b77c80e431ad1d8a806f6108b225a1cab9223e53e555776c612a24297117d28 - languageName: node - linkType: hard - -"object.entries@npm:^1.1.7": +"object.entries@npm:^1.1.0, object.entries@npm:^1.1.6, object.entries@npm:^1.1.7": version: 1.1.8 resolution: "object.entries@npm:1.1.8" dependencies: @@ -32497,18 +32297,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.0 || ^1.0.0, object.fromentries@npm:^2.0.6": - version: 2.0.6 - resolution: "object.fromentries@npm:2.0.6" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 - languageName: node - linkType: hard - -"object.fromentries@npm:^2.0.7": +"object.fromentries@npm:^2.0.0 || ^1.0.0, object.fromentries@npm:^2.0.6, object.fromentries@npm:^2.0.7": version: 2.0.8 resolution: "object.fromentries@npm:2.0.8" dependencies: @@ -34883,7 +34672,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.2.1, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: @@ -36096,6 +35885,15 @@ __metadata: languageName: node linkType: hard +"react-timing-hooks@npm:~4.0.2": + version: 4.0.2 + resolution: "react-timing-hooks@npm:4.0.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 95ebcaffe400f3e1af32bd82eb92258c5e1473e43a7edf65879692117946105bb21f4e91046fdc85c8c737997be1d8148bffacfde8a8a9ee835d81415048119e + languageName: node + linkType: hard + "react-virtuoso@npm:^4.7.1": version: 4.7.1 resolution: "react-virtuoso@npm:4.7.1" @@ -36530,18 +36328,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.4.3, regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.1": - version: 1.5.1 - resolution: "regexp.prototype.flags@npm:1.5.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - set-function-name: ^2.0.0 - checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 - languageName: node - linkType: hard - -"regexp.prototype.flags@npm:^1.5.2": +"regexp.prototype.flags@npm:^1.4.3, regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.2": version: 1.5.2 resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: @@ -37152,7 +36939,7 @@ __metadata: "@types/js-yaml": ^4.0.8 node-gyp: ^9.4.1 ts-node: ^10.9.2 - turbo: ^1.12.4 + turbo: latest languageName: unknown linkType: soft @@ -37284,18 +37071,6 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.0.1": - version: 1.0.1 - resolution: "safe-array-concat@npm:1.0.1" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.2.1 - has-symbols: ^1.0.3 - isarray: ^2.0.5 - checksum: 001ecf1d8af398251cbfabaf30ed66e3855127fbceee178179524b24160b49d15442f94ed6c0db0b2e796da76bb05b73bf3cc241490ec9c2b741b41d33058581 - languageName: node - linkType: hard - "safe-array-concat@npm:^1.1.2": version: 1.1.2 resolution: "safe-array-concat@npm:1.1.2" @@ -37329,17 +37104,6 @@ __metadata: languageName: node linkType: hard -"safe-regex-test@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-regex-test@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 - is-regex: ^1.1.4 - checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 - languageName: node - linkType: hard - "safe-regex-test@npm:^1.0.3": version: 1.0.3 resolution: "safe-regex-test@npm:1.0.3" @@ -37882,17 +37646,6 @@ __metadata: languageName: node linkType: hard -"set-function-name@npm:^2.0.0": - version: 2.0.1 - resolution: "set-function-name@npm:2.0.1" - dependencies: - define-data-property: ^1.0.1 - functions-have-names: ^1.2.3 - has-property-descriptors: ^1.0.0 - checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 - languageName: node - linkType: hard - "set-function-name@npm:^2.0.1": version: 2.0.2 resolution: "set-function-name@npm:2.0.2" @@ -39119,17 +38872,6 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.8": - version: 1.2.8 - resolution: "string.prototype.trim@npm:1.2.8" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 49eb1a862a53aba73c3fb6c2a53f5463173cb1f4512374b623bcd6b43ad49dd559a06fb5789bdec771a40fc4d2a564411c0a75d35fb27e76bbe738c211ecff07 - languageName: node - linkType: hard - "string.prototype.trim@npm:^1.2.9": version: 1.2.9 resolution: "string.prototype.trim@npm:1.2.9" @@ -39142,17 +38884,6 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimend@npm:1.0.7" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 2375516272fd1ba75992f4c4aa88a7b5f3c7a9ca308d963bcd5645adf689eba6f8a04ebab80c33e30ec0aefc6554181a3a8416015c38da0aa118e60ec896310c - languageName: node - linkType: hard - "string.prototype.trimend@npm:^1.0.8": version: 1.0.8 resolution: "string.prototype.trimend@npm:1.0.8" @@ -39164,17 +38895,6 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimstart@npm:1.0.7" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 13d0c2cb0d5ff9e926fa0bec559158b062eed2b68cd5be777ffba782c96b2b492944e47057274e064549b94dd27cf81f48b27a31fee8af5b574cff253e7eb613 - languageName: node - linkType: hard - "string.prototype.trimstart@npm:^1.0.8": version: 1.0.8 resolution: "string.prototype.trimstart@npm:1.0.8" @@ -40539,45 +40259,7 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:^10.9.1, ts-node@npm:~10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" - dependencies: - "@cspotcode/source-map-support": ^0.8.0 - "@tsconfig/node10": ^1.0.7 - "@tsconfig/node12": ^1.0.7 - "@tsconfig/node14": ^1.0.0 - "@tsconfig/node16": ^1.0.2 - acorn: ^8.4.1 - acorn-walk: ^8.1.1 - arg: ^4.1.0 - create-require: ^1.1.0 - diff: ^4.0.1 - make-error: ^1.1.1 - v8-compile-cache-lib: ^3.0.1 - yn: 3.1.1 - peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" - "@types/node": "*" - typescript: ">=2.7" - peerDependenciesMeta: - "@swc/core": - optional: true - "@swc/wasm": - optional: true - bin: - ts-node: dist/bin.js - ts-node-cwd: dist/bin-cwd.js - ts-node-esm: dist/bin-esm.js - ts-node-script: dist/bin-script.js - ts-node-transpile-only: dist/bin-transpile.js - ts-script: dist/bin-script-deprecated.js - checksum: 090adff1302ab20bd3486e6b4799e90f97726ed39e02b39e566f8ab674fd5bd5f727f43615debbfc580d33c6d9d1c6b1b3ce7d8e3cca3e20530a145ffa232c35 - languageName: node - linkType: hard - -"ts-node@npm:^10.9.2": +"ts-node@npm:^10.9.1, ts-node@npm:^10.9.2, ts-node@npm:~10.9.1": version: 10.9.2 resolution: "ts-node@npm:10.9.2" dependencies: @@ -40790,58 +40472,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-darwin-64@npm:1.12.4" +"turbo-darwin-64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-darwin-64@npm:2.0.11" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-darwin-arm64@npm:1.12.4" +"turbo-darwin-arm64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-darwin-arm64@npm:2.0.11" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-linux-64@npm:1.12.4" +"turbo-linux-64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-linux-64@npm:2.0.11" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-linux-arm64@npm:1.12.4" +"turbo-linux-arm64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-linux-arm64@npm:2.0.11" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-windows-64@npm:1.12.4" +"turbo-windows-64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-windows-64@npm:2.0.11" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.12.4": - version: 1.12.4 - resolution: "turbo-windows-arm64@npm:1.12.4" +"turbo-windows-arm64@npm:2.0.11": + version: 2.0.11 + resolution: "turbo-windows-arm64@npm:2.0.11" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:^1.12.4": - version: 1.12.4 - resolution: "turbo@npm:1.12.4" +"turbo@npm:latest": + version: 2.0.11 + resolution: "turbo@npm:2.0.11" dependencies: - turbo-darwin-64: 1.12.4 - turbo-darwin-arm64: 1.12.4 - turbo-linux-64: 1.12.4 - turbo-linux-arm64: 1.12.4 - turbo-windows-64: 1.12.4 - turbo-windows-arm64: 1.12.4 + turbo-darwin-64: 2.0.11 + turbo-darwin-arm64: 2.0.11 + turbo-linux-64: 2.0.11 + turbo-linux-arm64: 2.0.11 + turbo-windows-64: 2.0.11 + turbo-windows-arm64: 2.0.11 dependenciesMeta: turbo-darwin-64: optional: true @@ -40857,7 +40539,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: d387fb91af6ed0ea925201d3858180353c5d93be564829de2e22f48fe57124d1347d2abb8b99215901a305d4c6da4a0daf4c28afeec20fa1bc1ae2762c3b8d3d + checksum: a2fcb17b2549102dcd912799319a5c31cbabc3fcb76241bac1d2231ee4e1911789cd4e6b4eb050f9e8548ef89143ee77be59eb35b1843cf12b42f136ef176a0c languageName: node linkType: hard @@ -41028,17 +40710,6 @@ __metadata: languageName: node linkType: hard -"typed-array-buffer@npm:^1.0.0": - version: 1.0.0 - resolution: "typed-array-buffer@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.2.1 - is-typed-array: ^1.1.10 - checksum: 3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 - languageName: node - linkType: hard - "typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" @@ -41050,18 +40721,6 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-length@npm:^1.0.0": - version: 1.0.0 - resolution: "typed-array-byte-length@npm:1.0.0" - dependencies: - call-bind: ^1.0.2 - for-each: ^0.3.3 - has-proto: ^1.0.1 - is-typed-array: ^1.1.10 - checksum: b03db16458322b263d87a702ff25388293f1356326c8a678d7515767ef563ef80e1e67ce648b821ec13178dd628eb2afdc19f97001ceae7a31acf674c849af94 - languageName: node - linkType: hard - "typed-array-byte-length@npm:^1.0.1": version: 1.0.1 resolution: "typed-array-byte-length@npm:1.0.1" @@ -41075,19 +40734,6 @@ __metadata: languageName: node linkType: hard -"typed-array-byte-offset@npm:^1.0.0": - version: 1.0.0 - resolution: "typed-array-byte-offset@npm:1.0.0" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - has-proto: ^1.0.1 - is-typed-array: ^1.1.10 - checksum: 04f6f02d0e9a948a95fbfe0d5a70b002191fae0b8fe0fe3130a9b2336f043daf7a3dda56a31333c35a067a97e13f539949ab261ca0f3692c41603a46a94e960b - languageName: node - linkType: hard - "typed-array-byte-offset@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-byte-offset@npm:1.0.2" @@ -41102,17 +40748,6 @@ __metadata: languageName: node linkType: hard -"typed-array-length@npm:^1.0.4": - version: 1.0.4 - resolution: "typed-array-length@npm:1.0.4" - dependencies: - call-bind: ^1.0.2 - for-each: ^0.3.3 - is-typed-array: ^1.1.9 - checksum: 2228febc93c7feff142b8c96a58d4a0d7623ecde6c7a24b2b98eb3170e99f7c7eff8c114f9b283085cd59dcd2bd43aadf20e25bba4b034a53c5bb292f71f8956 - languageName: node - linkType: hard - "typed-array-length@npm:^1.0.6": version: 1.0.6 resolution: "typed-array-length@npm:1.0.6" @@ -41169,6 +40804,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:~5.4.5": + version: 5.4.5 + resolution: "typescript@npm:5.4.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 53c879c6fa1e3bcb194b274d4501ba1985894b2c2692fa079db03c5a5a7140587a1e04e1ba03184605d35f439b40192d9e138eb3279ca8eee313c081c8bcd9b0 + languageName: node + linkType: hard + "typescript@patch:typescript@~5.3.3#~builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=85af82" @@ -41179,6 +40824,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@~5.4.5#~builtin": + version: 5.4.5 + resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin::version=5.4.5&hash=85af82" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 2373c693f3b328f3b2387c3efafe6d257b057a142f9a79291854b14ff4d5367d3d730810aee981726b677ae0fd8329b23309da3b6aaab8263dbdccf1da07a3ba + languageName: node + linkType: hard + "typia@npm:5.3.3, typia@npm:^5.3.3": version: 5.3.3 resolution: "typia@npm:5.3.3" @@ -42025,7 +41680,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^9.0.0, uuid@npm:^9.0.1": +"uuid@npm:^9.0.0, uuid@npm:^9.0.1, uuid@npm:~9.0.1": version: 9.0.1 resolution: "uuid@npm:9.0.1" bin: @@ -43034,20 +42689,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": - version: 1.1.11 - resolution: "which-typed-array@npm:1.1.11" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 - languageName: node - linkType: hard - -"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15": +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" dependencies: