diff --git a/.changeset/bump-patch-1729526930133.md b/.changeset/bump-patch-1729526930133.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1729526930133.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/bump-patch-1729648473274.md b/.changeset/bump-patch-1729648473274.md new file mode 100644 index 000000000000..e1eaa7980afb --- /dev/null +++ b/.changeset/bump-patch-1729648473274.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Bump @rocket.chat/meteor version. diff --git a/.changeset/fair-colts-remain.md b/.changeset/fair-colts-remain.md new file mode 100644 index 000000000000..7ce003e50fd5 --- /dev/null +++ b/.changeset/fair-colts-remain.md @@ -0,0 +1,13 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/account-service": patch +"@rocket.chat/authorization-service": patch +"@rocket.chat/ddp-streamer": patch +"@rocket.chat/omnichannel-transcript": patch +"@rocket.chat/presence-service": patch +"@rocket.chat/queue-worker": patch +"@rocket.chat/stream-hub-service": patch +"rocketchat-services": patch +--- + +Bump meteor to 3.0.4 and Node version to 20.18.0 diff --git a/.changeset/forty-gorillas-kneel.md b/.changeset/forty-gorillas-kneel.md new file mode 100644 index 000000000000..42df0ed8c0e4 --- /dev/null +++ b/.changeset/forty-gorillas-kneel.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/apps-engine": patch +--- + +Deprecated the `from` field in the apps email bridge and made it optional, using the server's settings when the field is omitted diff --git a/.changeset/fuzzy-pans-share.md b/.changeset/fuzzy-pans-share.md deleted file mode 100644 index e689cb28df73..000000000000 --- a/.changeset/fuzzy-pans-share.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@rocket.chat/meteor": minor -"@rocket.chat/apps": minor -"@rocket.chat/core-typings": minor -"@rocket.chat/model-typings": minor ---- - -Adds a `source` field to livechat visitors, which stores the channel (eg API, widget, SMS, email-inbox, app) that's been used by the visitor to send messages. -Uses the new `source` field to assure each visitor is linked to a single source, so that each connection through a distinct channel creates a new visitor. diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 000000000000..0d6531af024a --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,176 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "@rocket.chat/meteor": "7.0.0-develop", + "rocketchat-services": "1.3.5", + "@rocket.chat/uikit-playground": "0.5.0", + "@rocket.chat/account-service": "0.4.8", + "@rocket.chat/authorization-service": "0.4.8", + "@rocket.chat/ddp-streamer": "0.3.8", + "@rocket.chat/omnichannel-transcript": "0.4.8", + "@rocket.chat/presence-service": "0.4.8", + "@rocket.chat/queue-worker": "0.4.8", + "@rocket.chat/stream-hub-service": "0.4.8", + "@rocket.chat/license": "0.2.8", + "@rocket.chat/network-broker": "0.1.0", + "@rocket.chat/omnichannel-services": "0.3.5", + "@rocket.chat/pdf-worker": "0.2.5", + "@rocket.chat/presence": "0.2.8", + "@rocket.chat/ui-theming": "0.3.0", + "@rocket.chat/account-utils": "0.0.2", + "@rocket.chat/agenda": "0.1.0", + "@rocket.chat/api-client": "0.2.8", + "@rocket.chat/apps": "0.1.8", + "@rocket.chat/apps-engine": "1.47.0-alpha", + "@rocket.chat/base64": "1.0.13", + "@rocket.chat/cas-validate": "0.0.2", + "@rocket.chat/core-services": "0.7.0", + "@rocket.chat/core-typings": "7.0.0-develop", + "@rocket.chat/cron": "0.1.8", + "@rocket.chat/ddp-client": "0.3.8", + "@rocket.chat/eslint-config": "0.7.0", + "@rocket.chat/favicon": "0.0.2", + "@rocket.chat/freeswitch": "0.0.1", + "@rocket.chat/fuselage-ui-kit": "11.0.0", + "@rocket.chat/gazzodown": "11.0.0", + "@rocket.chat/i18n": "0.8.0", + "@rocket.chat/instance-status": "0.1.8", + "@rocket.chat/jest-presets": "0.0.1", + "@rocket.chat/jwt": "0.1.1", + "@rocket.chat/livechat": "1.20.0", + "@rocket.chat/log-format": "0.0.2", + "@rocket.chat/logger": "0.0.2", + "@rocket.chat/message-parser": "0.31.31", + "@rocket.chat/mock-providers": "0.1.3", + "@rocket.chat/model-typings": "0.8.0", + "@rocket.chat/models": "0.3.0", + "@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.27", + "@rocket.chat/random": "1.2.2", + "@rocket.chat/release-action": "2.2.3", + "@rocket.chat/release-changelog": "0.1.0", + "@rocket.chat/rest-typings": "7.0.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.2", + "@rocket.chat/tracing": "0.0.1", + "@rocket.chat/ui-avatar": "7.0.0", + "@rocket.chat/ui-client": "11.0.0", + "@rocket.chat/ui-composer": "0.3.0", + "@rocket.chat/ui-contexts": "11.0.0", + "@rocket.chat/ui-kit": "0.36.1", + "@rocket.chat/ui-video-conf": "11.0.0", + "@rocket.chat/ui-voip": "1.0.0", + "@rocket.chat/web-ui-registration": "11.0.0" + }, + "changesets": [ + "brown-pants-press", + "bump-patch-1729526930133", + "bump-patch-1729648473274", + "chilled-boats-sip", + "chilled-files-relate", + "chilly-flowers-brake", + "cool-dryers-judge", + "dry-taxis-cry", + "dull-singers-move", + "e2ee-composer-freeze", + "e2ee-modal-keys", + "fair-bees-wash", + "fair-seahorses-laugh", + "fifty-mails-admire", + "five-suns-tickle", + "fluffy-knives-count", + "forty-actors-care", + "forty-needles-sit", + "forty-pants-roll", + "four-experts-compare", + "four-snakes-deny", + "fuzzy-cherries-buy", + "gentle-kings-greet", + "giant-spiders-train", + "gold-falcons-hear", + "gold-knives-sparkle", + "gorgeous-houses-sneeze", + "grumpy-lamps-beg", + "grumpy-weeks-appear", + "heavy-apricots-wash", + "heavy-carrots-reflect", + "hot-socks-play", + "hungry-icons-try", + "khaki-boxes-suffer", + "kind-clocks-smash", + "kind-eels-brush", + "large-bikes-brake", + "large-pillows-brush", + "late-hats-carry", + "lemon-tables-nail", + "little-bottles-peel", + "little-gifts-do", + "lovely-trees-call", + "many-carrots-care", + "many-files-turn", + "mean-readers-join", + "modern-trees-draw", + "moody-phones-admire", + "nice-vans-design", + "olive-dogs-jam", + "perfect-wolves-impress", + "pink-wombats-wait", + "plenty-hairs-camp", + "poor-falcons-doubt", + "proud-bugs-cry", + "purple-papayas-collect", + "purple-tools-heal", + "quick-moles-jump", + "quiet-kings-rhyme", + "rare-hats-lie", + "real-avocados-sneeze", + "real-plants-mix", + "red-crews-behave", + "rude-dodos-agree", + "selfish-experts-develop", + "selfish-schools-leave", + "seven-hotels-collect", + "sharp-adults-think", + "sharp-forks-give", + "shiny-falcons-vanish", + "six-horses-sin", + "sixty-moons-walk", + "sixty-owls-arrive", + "sixty-vans-grab", + "slow-crabs-run", + "slow-rules-bow", + "smooth-horses-draw", + "soft-ducks-build", + "soft-planets-cross", + "spicy-eggs-march", + "strange-spies-clean", + "strong-waves-add", + "swift-penguins-help", + "tame-dolls-know", + "tasty-goats-deny", + "ten-houses-repair", + "tender-cheetahs-teach", + "tender-readers-run", + "tender-turkeys-breathe", + "thick-waves-try", + "thirty-trainers-swim", + "three-avocados-search", + "three-crews-allow", + "tidy-suns-move", + "tiny-rice-train", + "tough-rings-kneel", + "tricky-horses-swim", + "twenty-ways-think", + "two-emus-wash", + "two-geckos-train", + "unlucky-ducks-arrive", + "witty-apples-pretend", + "yellow-jobs-serve", + "young-maps-push" + ] +} diff --git a/.changeset/smart-radios-reflect.md b/.changeset/smart-radios-reflect.md new file mode 100644 index 000000000000..58ea7413e51c --- /dev/null +++ b/.changeset/smart-radios-reflect.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-services": patch +--- + +stops calling an object through proxy calling getQueueWorker diff --git a/.github/actions/update-version-durability/index.js b/.github/actions/update-version-durability/index.js index d96fd7ee4554..b05b9e9ca769 100644 --- a/.github/actions/update-version-durability/index.js +++ b/.github/actions/update-version-durability/index.js @@ -5,7 +5,7 @@ import semver from 'semver'; import crypto from 'crypto'; import fs from 'fs/promises'; import BeautyHtml from 'beauty-html'; -import { DOMParser } from 'xmldom'; +import { DOMParser } from '@xmldom/xmldom'; import core from '@actions/core'; import { Octokit } from '@octokit/rest'; diff --git a/.github/workflows/ci-deploy-gh-pages.yml b/.github/workflows/ci-deploy-gh-pages.yml index 6da5693303b4..ce03f20383d2 100644 --- a/.github/workflows/ci-deploy-gh-pages.yml +++ b/.github/workflows/ci-deploy-gh-pages.yml @@ -17,7 +17,7 @@ jobs: - name: Setup NodeJS uses: ./.github/actions/setup-node with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index 8f5d258ef165..0de23fb966b5 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -305,7 +305,11 @@ jobs: include-hidden-files: true - name: Show server logs if E2E test failed - if: failure() + if: failure() && inputs.release == 'ee' + run: docker compose -f docker-compose-ci.yml logs + + - name: Show server logs if E2E test failed + if: failure() && inputs.release != 'ee' run: docker compose -f docker-compose-ci.yml logs rocketchat - name: Extract e2e:ee:coverage diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 449406bfc496..9ad610e8ac16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -200,7 +200,7 @@ jobs: uses: ./.github/actions/setup-node if: github.event.action != 'closed' with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml index cd6fc3dec3f2..b3ead30e8966 100644 --- a/.github/workflows/new-release.yml +++ b/.github/workflows/new-release.yml @@ -34,7 +34,7 @@ jobs: - name: Setup NodeJS uses: ./.github/actions/setup-node with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/pr-update-description.yml b/.github/workflows/pr-update-description.yml index 66a8e4436a34..7a0d8650a97f 100644 --- a/.github/workflows/pr-update-description.yml +++ b/.github/workflows/pr-update-description.yml @@ -21,7 +21,7 @@ jobs: - name: Setup NodeJS uses: ./.github/actions/setup-node with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 33f2de48feeb..2f803576ef51 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -24,7 +24,7 @@ jobs: - name: Setup NodeJS uses: ./.github/actions/setup-node with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml index ad64416da04c..d2d22a3fc6c5 100644 --- a/.github/workflows/release-candidate.yml +++ b/.github/workflows/release-candidate.yml @@ -15,7 +15,7 @@ jobs: - name: Setup NodeJS uses: ./.github/actions/setup-node with: - node-version: 20.17.0 + node-version: 20.18.0 deno-version: 1.37.1 cache-modules: true install: true diff --git a/.github/workflows/update-version-durability.yml b/.github/workflows/update-version-durability.yml index bdf6b75e0f5b..410734f94e2b 100644 --- a/.github/workflows/update-version-durability.yml +++ b/.github/workflows/update-version-durability.yml @@ -14,22 +14,22 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v4.0.4 - with: - node-version: '20.17.0' + - name: Use Node.js + uses: actions/setup-node@v4.0.4 + with: + node-version: '20.18.0' - - name: Install dependencies - run: | - cd ./.github/actions/update-version-durability - npm install + - name: Install dependencies + run: | + cd ./.github/actions/update-version-durability + npm install - - name: Update Version Durability - uses: ./.github/actions/update-version-durability - with: - GH_TOKEN: ${{ secrets.CI_PAT }} - D360_TOKEN: ${{ secrets.D360_TOKEN }} - D360_ARTICLE_ID: 800f8d52-409d-478d-b560-f82a2c0eb7fb - PUBLISH: true + - name: Update Version Durability + uses: ./.github/actions/update-version-durability + with: + GH_TOKEN: ${{ secrets.CI_PAT }} + D360_TOKEN: ${{ secrets.D360_TOKEN }} + D360_ARTICLE_ID: 800f8d52-409d-478d-b560-f82a2c0eb7fb + PUBLISH: true diff --git a/apps/meteor/.docker-mongo/Dockerfile b/apps/meteor/.docker-mongo/Dockerfile index 17784e1769a3..560fde4a69dc 100644 --- a/apps/meteor/.docker-mongo/Dockerfile +++ b/apps/meteor/.docker-mongo/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.17.0-bullseye-slim +FROM node:20.18.0-bullseye-slim LABEL maintainer="buildmaster@rocket.chat" diff --git a/apps/meteor/.docker/Dockerfile b/apps/meteor/.docker/Dockerfile index e225594ec44f..b252059a2c58 100644 --- a/apps/meteor/.docker/Dockerfile +++ b/apps/meteor/.docker/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.17.0-alpine3.20 +FROM node:20.18.0-alpine3.20 LABEL maintainer="buildmaster@rocket.chat" diff --git a/apps/meteor/.docker/Dockerfile.debian b/apps/meteor/.docker/Dockerfile.debian index c8ede9db3cae..22134532ece0 100644 --- a/apps/meteor/.docker/Dockerfile.debian +++ b/apps/meteor/.docker/Dockerfile.debian @@ -2,7 +2,7 @@ ARG DENO_VERSION="1.37.1" FROM denoland/deno:bin-${DENO_VERSION} as deno -FROM node:20.17.0-bullseye-slim +FROM node:20.18.0-bullseye-slim LABEL maintainer="buildmaster@rocket.chat" @@ -30,21 +30,27 @@ ENV DEPLOY_METHOD=docker \ RUN aptMark="$(apt-mark showmanual)" \ && apt-get install -y --no-install-recommends g++ make python3 ca-certificates \ - && cd /app/bundle/programs/server \ + && apt-mark auto '.*' > /dev/null \ + && apt-mark manual $aptMark > /dev/null + +USER rocketchat + +RUN cd /app/bundle/programs/server \ && npm install \ && cd npm/node_modules/isolated-vm \ && npm install \ - && apt-mark auto '.*' > /dev/null \ - && apt-mark manual $aptMark > /dev/null \ - && find /usr/local -type f -executable -exec ldd '{}' ';' \ + && npm cache clear --force + +USER root + +RUN find /usr/local -type f -executable -exec ldd '{}' ';' \ | awk '/=>/ { print $(NF-1) }' \ | sort -u \ | xargs -r dpkg-query --search \ | cut -d: -f1 \ | sort -u \ | xargs -r apt-mark manual \ - && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ - && npm cache clear --force + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false USER rocketchat diff --git a/apps/meteor/.meteor/packages b/apps/meteor/.meteor/packages index 70518d252df9..ead1cc41fd09 100644 --- a/apps/meteor/.meteor/packages +++ b/apps/meteor/.meteor/packages @@ -14,7 +14,7 @@ rocketchat:streamer rocketchat:version rocketchat:user-presence -accounts-base@3.0.2 +accounts-base@3.0.3 accounts-facebook@1.3.4 accounts-github@1.5.1 accounts-google@1.4.1 @@ -27,14 +27,14 @@ google-oauth@1.4.5 oauth@3.0.0 oauth2@1.3.3 -check@1.4.2 +check@1.4.4 ddp-rate-limiter@1.2.2 rate-limit@1.1.2 email@3.1.0 meteor-base@1.5.2 ddp-common@1.4.4 -webapp@2.0.1 +webapp@2.0.3 mongo@2.0.2 @@ -59,7 +59,7 @@ tracker@1.3.4 reactive-dict@1.3.2 reactive-var@1.0.13 -babel-compiler@7.11.0 +babel-compiler@7.11.1 standard-minifier-css@1.9.3 dynamic-import@0.7.4 ecmascript@0.16.9 diff --git a/apps/meteor/.meteor/release b/apps/meteor/.meteor/release index c41cba9c61a2..b1e86a359f7c 100644 --- a/apps/meteor/.meteor/release +++ b/apps/meteor/.meteor/release @@ -1 +1 @@ -METEOR@3.0.3 +METEOR@3.0.4 diff --git a/apps/meteor/.meteor/versions b/apps/meteor/.meteor/versions index c41710f5aa16..0f8f733b3148 100644 --- a/apps/meteor/.meteor/versions +++ b/apps/meteor/.meteor/versions @@ -1,4 +1,4 @@ -accounts-base@3.0.2 +accounts-base@3.0.3 accounts-facebook@1.3.4 accounts-github@1.5.1 accounts-google@1.4.1 @@ -8,24 +8,24 @@ accounts-password@3.0.2 accounts-twitter@1.5.2 allow-deny@2.0.0 autoupdate@2.0.0 -babel-compiler@7.11.0 +babel-compiler@7.11.1 babel-runtime@1.5.2 base64@1.0.13 binary-heap@1.0.12 boilerplate-generator@2.0.0 callback-hook@1.6.0 -check@1.4.2 +check@1.4.4 core-runtime@1.0.0 ddp@1.4.2 -ddp-client@3.0.1 +ddp-client@3.0.2 ddp-common@1.4.4 ddp-rate-limiter@1.2.2 -ddp-server@3.0.1 +ddp-server@3.0.2 diff-sequence@1.1.3 dispatch:run-as-user@1.1.1 dynamic-import@0.7.4 ecmascript@0.16.9 -ecmascript-runtime@0.8.2 +ecmascript-runtime@0.8.3 ecmascript-runtime-client@0.12.2 ecmascript-runtime-server@0.11.1 ejson@1.1.4 @@ -51,7 +51,7 @@ meteorhacks:inject-initial@1.0.5 minifier-css@2.0.0 minimongo@2.0.1 modern-browsers@0.1.11 -modules@0.20.1 +modules@0.20.2 modules-runtime@0.13.2 mongo@2.0.2 mongo-decimal@0.1.4-beta300.7 @@ -89,8 +89,8 @@ tracker@1.3.4 twitter-oauth@1.3.4 typescript@5.4.3 underscore@1.6.4 -url@1.3.3 -webapp@2.0.1 +url@1.3.4 +webapp@2.0.3 webapp-hashing@1.1.2 zodern:caching-minifier@0.5.0 zodern:standard-minifier-js@5.2.0 diff --git a/apps/meteor/.mocharc.js b/apps/meteor/.mocharc.js index fb5324f4c0fd..aa092068a9b0 100644 --- a/apps/meteor/.mocharc.js +++ b/apps/meteor/.mocharc.js @@ -32,6 +32,7 @@ module.exports = { 'tests/unit/app/**/*.tests.js', 'tests/unit/app/**/*.tests.ts', 'tests/unit/lib/**/*.tests.ts', + 'server/routes/avatar/**/*.spec.ts', 'tests/unit/lib/**/*.spec.ts', 'tests/unit/server/**/*.tests.ts', 'tests/unit/server/**/*.spec.ts', diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 2fd9252f1ba1..55bba48a1b49 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,353 @@ # @rocket.chat/meteor +## 7.0.0-rc.2 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +-
Updated dependencies []: + + - @rocket.chat/core-typings@7.0.0-rc.2 + - @rocket.chat/rest-typings@7.0.0-rc.2 + - @rocket.chat/license@1.0.0-rc.2 + - @rocket.chat/omnichannel-services@0.3.6-rc.2 + - @rocket.chat/pdf-worker@0.2.6-rc.2 + - @rocket.chat/presence@0.2.9-rc.2 + - @rocket.chat/api-client@0.2.9-rc.2 + - @rocket.chat/apps@0.2.0-rc.2 + - @rocket.chat/core-services@0.7.1-rc.2 + - @rocket.chat/cron@0.1.9-rc.2 + - @rocket.chat/freeswitch@1.0.0-rc.2 + - @rocket.chat/fuselage-ui-kit@12.0.0-rc.2 + - @rocket.chat/gazzodown@12.0.0-rc.2 + - @rocket.chat/model-typings@1.0.0-rc.2 + - @rocket.chat/ui-contexts@12.0.0-rc.2 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/network-broker@0.1.1-rc.2 + - @rocket.chat/models@1.0.0-rc.2 + - @rocket.chat/ui-theming@0.4.0-rc.0 + - @rocket.chat/ui-avatar@8.0.0-rc.2 + - @rocket.chat/ui-client@12.0.0-rc.2 + - @rocket.chat/ui-video-conf@12.0.0-rc.2 + - @rocket.chat/ui-voip@2.0.0-rc.2 + - @rocket.chat/web-ui-registration@12.0.0-rc.2 + - @rocket.chat/instance-status@0.1.9-rc.2 +
+ +## 7.0.0-rc.1 + +### Patch Changes + +- Bump @rocket.chat/meteor version. + +-
Updated dependencies []: + + - @rocket.chat/core-typings@7.0.0-rc.1 + - @rocket.chat/rest-typings@7.0.0-rc.1 + - @rocket.chat/license@1.0.0-rc.1 + - @rocket.chat/omnichannel-services@0.3.6-rc.1 + - @rocket.chat/pdf-worker@0.2.6-rc.1 + - @rocket.chat/presence@0.2.9-rc.1 + - @rocket.chat/api-client@0.2.9-rc.1 + - @rocket.chat/apps@0.2.0-rc.1 + - @rocket.chat/core-services@0.7.1-rc.1 + - @rocket.chat/cron@0.1.9-rc.1 + - @rocket.chat/freeswitch@1.0.0-rc.1 + - @rocket.chat/fuselage-ui-kit@12.0.0-rc.1 + - @rocket.chat/gazzodown@12.0.0-rc.1 + - @rocket.chat/model-typings@1.0.0-rc.1 + - @rocket.chat/ui-contexts@12.0.0-rc.1 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/network-broker@0.1.1-rc.1 + - @rocket.chat/models@1.0.0-rc.1 + - @rocket.chat/ui-theming@0.4.0-rc.0 + - @rocket.chat/ui-avatar@8.0.0-rc.1 + - @rocket.chat/ui-client@12.0.0-rc.1 + - @rocket.chat/ui-video-conf@12.0.0-rc.1 + - @rocket.chat/ui-voip@2.0.0-rc.1 + - @rocket.chat/web-ui-registration@12.0.0-rc.1 + - @rocket.chat/instance-status@0.1.9-rc.1 +
+ +## 7.0.0-rc.0 + +### Major Changes + +- ([#33316](https://github.com/RocketChat/Rocket.Chat/pull/33316)) Changes some displays to reflect new rules for private apps and adds a new modal before uploading a private app + +- ([#32212](https://github.com/RocketChat/Rocket.Chat/pull/32212)) Fixed broken translation in "Forgot Password" e-mail + +- ([#33503](https://github.com/RocketChat/Rocket.Chat/pull/33503)) Adds modal confirmation to enable and disable End-to-end encryption + + Adds a reset room key option to the modal that disables End-to-end encryption, this is useful when all the members of a room lose their room E2EE keys + +- ([#33474](https://github.com/RocketChat/Rocket.Chat/pull/33474)) Removes deprecated endpoint `pw.getPolicyReset`. Moving forward, use the `pw.getPolicy` endpoint. + +- ([#32162](https://github.com/RocketChat/Rocket.Chat/pull/32162)) As per MongoDB Lifecycle Schedules ([mongodb.com/legal/support-policy/lifecycles](https://www.mongodb.com/legal/support-policy/lifecycles)) we're removing official support to MongoDB version 4.4 that has reached end of life in February 2024. + + We recommend upgrading to at least MongoDB 6.0+, though 5.0 is still a supported version. + + Here are official docs on how to upgrade to some of the supported versions: + + - [mongodb.com/docs/manual/release-notes/5.0-upgrade-replica-set](https://www.mongodb.com/docs/manual/release-notes/5.0-upgrade-replica-set/) + - [mongodb.com/docs/manual/release-notes/6.0-upgrade-replica-set](https://www.mongodb.com/docs/manual/release-notes/6.0-upgrade-replica-set/) + - [mongodb.com/docs/manual/release-notes/7.0-upgrade-replica-set](https://www.mongodb.com/docs/manual/release-notes/7.0-upgrade-replica-set/) + +- ([#33333](https://github.com/RocketChat/Rocket.Chat/pull/33333)) 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. + +- ([#32162](https://github.com/RocketChat/Rocket.Chat/pull/32162)) Added MongoDB 7.0 support + +- ([#33449](https://github.com/RocketChat/Rocket.Chat/pull/33449)) Removes deprecated method `livechat:webhookTest`. Moving forward use the endpoint `livechat/webhook.test`. + +- ([#33373](https://github.com/RocketChat/Rocket.Chat/pull/33373)) This adjustment removes the deprecated method `livechat:searchAgent`. Moving forward, use `livechat/users/agent/:_id` endpoint. + +- ([#33442](https://github.com/RocketChat/Rocket.Chat/pull/33442)) Removes deprecated `livechat:getAnalyticsOverviewData` method. Moving forward use the `livechat/analytics/overview` endpoint. + +- ([#31889](https://github.com/RocketChat/Rocket.Chat/pull/31889)) Removed upsert behavior on `users.update` endpoint (`joinDefaultChannels` param or empty `userId` are not allowed anymore) + +- ([#32159](https://github.com/RocketChat/Rocket.Chat/pull/32159)) Api login should not suggest which credential is wrong (password/username) + + Failed login attemps will always return `Unauthorized` instead of the internal fail reason + +- ([#31117](https://github.com/RocketChat/Rocket.Chat/pull/31117)) Adds a new set of permissions to provide a more granular control for the creation and deletion of rooms within teams + + - `create-team-channel`: controls the creations of public rooms within teams, it is checked within the team's main room scope and overrides the global `create-c` permission check. That is, granting this permission to a role allows users to create channels in teams even if they do not have the permission to create channels globally; + - `create-team-group`: controls the creations of private rooms within teams, it is checked within the team's main room scope and overrides the global `create-p` permission check. That is, granting this permission to a role allows users to create groups in teams even if they do not have the permission to create groups globally; + - `delete-team-channel`: controls the deletion of public rooms within teams, it is checked within the team's main room scope and complements the global `delete-c` permission check. That is, users must have both permissions (`delete-c` in the channel scope and `delete-team-channel` in its team scope) in order to be able to delete a channel in a team; + - `delete-team-group`: controls the deletion of private rooms within teams, it is checked within the team's main room scope and complements the global `delete-p` permission check. That is, users must have both permissions (`delete-p` in the group scope and `delete-team-group` in its team scope) in order to be able to delete a group in a team;; + + Renames `add-team-channel` permission (used for adding existing rooms to teams) to `move-room-to-team`, since it is applied to groups and channels. + +- ([#33391](https://github.com/RocketChat/Rocket.Chat/pull/33391)) Removed deprecated method `livechat:loginByToken`. Moving forward, use the endpoint `livechat/visitor/:token`. + +- ([#33473](https://github.com/RocketChat/Rocket.Chat/pull/33473)) Removes deprecated method `getPasswordPolicy`. Moving forward, use the endpoint `pw.getPolicy`. + +- ([#33210](https://github.com/RocketChat/Rocket.Chat/pull/33210)) Removes private App installation via URL method following a deprecation warning. + +- ([#33429](https://github.com/RocketChat/Rocket.Chat/pull/33429)) Removed deprecated methods `livechat:saveTrigger` and `livechat:removeTrigger`. Moving forward use the endpoints `livechat/triggers (POST)` and `livechat/triggers/:_id (DELETE)` respectively. + +- ([#33465](https://github.com/RocketChat/Rocket.Chat/pull/33465)) Removes deprecated method `addOAuthApp`. Moving forward, use the endpoint `oauth-apps.create` instead. + +- ([#33472](https://github.com/RocketChat/Rocket.Chat/pull/33472)) Removes deprecated `deleteMessage` method. Moving forward, use the `chat.delete` endpoint. + +- ([#33447](https://github.com/RocketChat/Rocket.Chat/pull/33447)) Removes deprecated method `livechat:saveInfo`. Moving forward use the enpoint `livechat/room/save.info`. + +- ([#31438](https://github.com/RocketChat/Rocket.Chat/pull/31438)) Upgrades the version of the Meteor framework to 3.0 + + The main reason behind this is the upgrade of the Node.js version, where version 14 will be removed and version 20 will be used instead. + + Internally, significant changes have been made, mostly due to the removal of fibers. + + As a result, it was necessary to adapt our code to work with the new version. + + No functionality should have been affected by this, but if you are running Rocket.Chat in unconventional ways, please note that you need to upgrade your Node.js version. + +- ([#33426](https://github.com/RocketChat/Rocket.Chat/pull/33426)) Removed deprecated method `livechat:getNextAgent`. Moving forward, use the `livechat/agent.next/:token` endpoint. + +- ([#33526](https://github.com/RocketChat/Rocket.Chat/pull/33526)) Removes deprecated method `livechat:setDepartmentForVisitor`. Moving forward, use the endpoint `livechat/visitor`. + +- ([#33241](https://github.com/RocketChat/Rocket.Chat/pull/33241)) Adds restrictions to air-gapped environments without a license + +- ([#33042](https://github.com/RocketChat/Rocket.Chat/pull/33042)) Removes `view-history` permission due to lack of usage + +- ([#33448](https://github.com/RocketChat/Rocket.Chat/pull/33448)) Removes the deprecated method `livechat:saveAppearance`. Moving forward use the endpoint `livechat/appearance`. + +- ([#33445](https://github.com/RocketChat/Rocket.Chat/pull/33445)) Removes deprecated `livechat:getAgentOverviewData` method. Moving forward use `livechat/analytics/agent-overview` endpoint. + +- ([#33444](https://github.com/RocketChat/Rocket.Chat/pull/33444)) Removes deprecated method `livechat:setCustomField`. The custom fields can be directly set via the `livechat/visitor` endpoint. + +- ([#33471](https://github.com/RocketChat/Rocket.Chat/pull/33471)) Removes deprecated endpoint `channels.images`. Moving forward, use `rooms.images` endpoint. + +- ([#33650](https://github.com/RocketChat/Rocket.Chat/pull/33650)) Changes custom emoji listing endpoint by moving query params from the 'query' attribute to standard query parameters. + +- ([#32856](https://github.com/RocketChat/Rocket.Chat/pull/32856)) Adds a new collection to store all the workspace cloud tokens to defer the race condition management to MongoDB instead of having to handle it within the settings cache. + Removes the Cloud_Workspace_Access_Token & Cloud_Workspace_Access_Token_Expires_At settings since they are not going to be used anymore. +- ([#33423](https://github.com/RocketChat/Rocket.Chat/pull/33423)) Removed deprecated methods `livechat:removeAgent`, `livechat:removeManager` and `livechat:removeDepartment`. Moving forward, use `livechat/users/agent/:_id`, and `livechat/users/manager/:_id` and `livechat/department/:_id` respectively.` + +- ([#31438](https://github.com/RocketChat/Rocket.Chat/pull/31438)) Node.js 20.x support + +- ([#32154](https://github.com/RocketChat/Rocket.Chat/pull/32154)) Removed the ability to import data in the HipChat Enterprise format, as it was discontinued over five years ago. + +- ([#33419](https://github.com/RocketChat/Rocket.Chat/pull/33419)) Changes End-to-end encryption default setting to enable mention in encrypted messages + +- ([#33451](https://github.com/RocketChat/Rocket.Chat/pull/33451)) Removes deprecated method `livechat:saveIntegration`. Moving forward, use the endpoint `omnichannel/integrations (POST)`. + +- ([#33443](https://github.com/RocketChat/Rocket.Chat/pull/33443)) Removed deprecated method `livechat:saveSurveyFeedback`. Moving forward use the endpoint `livechat/room.survey`. + +- Removed the deprecated "Compatible Sandbox" option from integration scripts and the dependencies that this sandbox mode relied on. + +- ([#33453](https://github.com/RocketChat/Rocket.Chat/pull/33453)) Removes deprecated `livechat/inquiries.queued` endpoint. Moving forward use the `livechat/inquiries.queuedForUser` endpoint. + +- ([#31438](https://github.com/RocketChat/Rocket.Chat/pull/31438)) Remove linkedin oauth package, now linkedin oauth must to me configured as custom oauth + +- ([#32285](https://github.com/RocketChat/Rocket.Chat/pull/32285)) Fixed issue with LDAP sync triggering multiple cron jobs in case an invalid sync interval is provided + +- ([#33328](https://github.com/RocketChat/Rocket.Chat/pull/33328)) Allows authorized users to reset the encryption key for end-to-end encrypted rooms. This aims to prevent situations where all users of a room have lost the encryption key, and as such, the access to the room. + +- ([#33539](https://github.com/RocketChat/Rocket.Chat/pull/33539)) Removes deprecated method `livechat:registerAgent`. Moving forward, use the endpoint `livechat/visitor`. + +- ([#33520](https://github.com/RocketChat/Rocket.Chat/pull/33520)) Fixes a behavior of E2EE room creation that allowed any user on the room to define room keys before the room creator, causing race conditions. + +- ([#33329](https://github.com/RocketChat/Rocket.Chat/pull/33329)) Randomizes `e2eKeyId` generation instead of derive it from encoded key. Previously, we used the stringified & encoded version of the key to extract a keyID, however this generated the same keyID for all rooms. As we didn't use this keyID, and rooms didn't have the capability of having multiple keys, this was harmless. + This PR introduces a new way of generating that identifier, making it random and unique, so multiple room keys can be used on the same room as long as the keyID is different. + + NOTE: new E2EE rooms created _after_ this PR is merged will not be compatible with older versions of Rocket.Chat. Old rooms created before this update will continue to be compatible. + +- ([#33361](https://github.com/RocketChat/Rocket.Chat/pull/33361)) Change the E2EE setting - "Access unencrypted content in encrypted rooms" default value, making the current behavior not allow to send unencrypted messages in end-to-end encrypted channels. + +- ([#33470](https://github.com/RocketChat/Rocket.Chat/pull/33470)) Removes deprecated endpoints `licenses.isEnterprise` and `licenses.get`. Moving forward use the endpoint `licenses.info.` + +- ([#33371](https://github.com/RocketChat/Rocket.Chat/pull/33371)) This adjustment removes deprecated `livechat:getCustomFields` method. Moving forward use the `livechat/custom-fields` endpoint. + +- ([#33605](https://github.com/RocketChat/Rocket.Chat/pull/33605)) Updates End-to-end settings translations and removes beta wording + +- ([#33572](https://github.com/RocketChat/Rocket.Chat/pull/33572)) Removes the ability of changing room's encryption status from the `key` icon placed on the room's header. Icon's purpose is now only informative, showing when a room uses E2EE. Use the kebab menu to enable/disable E2EE. + +- ([#33432](https://github.com/RocketChat/Rocket.Chat/pull/33432)) Removed deprecated methods `livechat:requestTranscript` and `livechat:discardTranscript`. Moving forward use `livechat/transcript/:rid` endpoint's POST and DELETE methods. + +- ([#31383](https://github.com/RocketChat/Rocket.Chat/pull/31383)) No longer shows archived rooms in `rooms.autocomplete.channelAndPrivate` endpoint + +- ([#32532](https://github.com/RocketChat/Rocket.Chat/pull/32532)) Removed `meteor/check` from `chat` endpoints + +- ([#33434](https://github.com/RocketChat/Rocket.Chat/pull/33434)) Renames the settings group 'Voice Channel' to 'Omnichannel voice channel (VoIP)' to better reflect its responsibility. + +- ([#33427](https://github.com/RocketChat/Rocket.Chat/pull/33427)) Removed deprecated method `livechat:pageVisited`. Moving forward, use the `livechat/page.visited` endpoint. + +- ([#33446](https://github.com/RocketChat/Rocket.Chat/pull/33446)) Removed deprecated method `livechat:saveDepartmentAgents`. Moving forward, use the endpoint `livechat/department/:_id/agents`. + +- ([#33630](https://github.com/RocketChat/Rocket.Chat/pull/33630)) Changes the payload of the startImport endpoint to decrease the amount of data it requires + +- ([#33238](https://github.com/RocketChat/Rocket.Chat/pull/33238)) Adds new empty states for the marketplace view + +- ([#33452](https://github.com/RocketChat/Rocket.Chat/pull/33452)) Removes deprecated method `livechat:sendOfflineMessage`. Moving forward, use the endpoint `livechat/offline.message`. + +- ([#33450](https://github.com/RocketChat/Rocket.Chat/pull/33450)) Removes deprecated method `livechat:getAgentData`. Moving forward use the endpoint `livechat/agent.info/:rid/:token`. + +- ([#33461](https://github.com/RocketChat/Rocket.Chat/pull/33461)) Removes deprecated endpoint `livechat/room.visitor`. + +### Minor Changes + +- ([#32194](https://github.com/RocketChat/Rocket.Chat/pull/32194)) Adds methods to the Apps-Engine to interact with unread messages to enhance message capabilities on Apps. + +- ([#33377](https://github.com/RocketChat/Rocket.Chat/pull/33377)) Send messages with encrypted attachments to mobile notification service + +- ([#33489](https://github.com/RocketChat/Rocket.Chat/pull/33489)) Adds `Recent` button on the new sidebar Search section to replicate the previous behavior of focusing the search bar - show recent chats. + +- ([#33562](https://github.com/RocketChat/Rocket.Chat/pull/33562)) Introduces a new featured action on the room header for action buttons using the non-default category to enhance user accessibility. + +- ([#33569](https://github.com/RocketChat/Rocket.Chat/pull/33569)) Adds a `source` field to livechat visitors, which stores the channel (eg API, widget, SMS, email-inbox, app) that's been used by the visitor to send messages. + Uses the new `source` field to assure each visitor is linked to a single source, so that each connection through a distinct channel creates a new visitor. +- ([#33592](https://github.com/RocketChat/Rocket.Chat/pull/33592)) Adds ability to collapse/expand sidebar groups + +- ([#33294](https://github.com/RocketChat/Rocket.Chat/pull/33294)) Improves the accessibility of the report user modal by adding an appropriate label, description, and ARIA attributes. + +- ([#33283](https://github.com/RocketChat/Rocket.Chat/pull/33283)) Adds a warning to inform users they are about to send unencrypted messages in an E2E Encrypted room if they have the `Unencrypted messages in encrypted rooms` setting enabled. + +- ([#33066](https://github.com/RocketChat/Rocket.Chat/pull/33066)) Introduces new property `category` for Rocket.Chat Apps to register UI action buttons. This property is used to group buttons in the UI. + +- ([#33386](https://github.com/RocketChat/Rocket.Chat/pull/33386)) Fixes the departments filter on the omnichannel current chats page by ensuring that the selected department is fetched and + added if it was not part of the initial department list. This prevents the filter from becoming blank and avoids potential + UX issues. +- ([#33598](https://github.com/RocketChat/Rocket.Chat/pull/33598)) Adds a new setting to allow mapping LDAP attributes to the user's extension + +- ([#33496](https://github.com/RocketChat/Rocket.Chat/pull/33496)) Applied category reorder from admin setting also on new sidebar - Enhanced navigation feature preview + +- ([#33433](https://github.com/RocketChat/Rocket.Chat/pull/33433)) Added support for interacting with add-ons issued in the license + +- ([#33483](https://github.com/RocketChat/Rocket.Chat/pull/33483)) Introduces new visual components into marketplace pages to inform an add-on necessity into the workspace. + +### Patch Changes + +- ([#32648](https://github.com/RocketChat/Rocket.Chat/pull/32648) by [@AllanPazRibeiro](https://github.com/AllanPazRibeiro)) This adjustment removes the deprecated `eraseRoom` method. Moving forward, use the `room.delete` endpoint to delete rooms. + +- ([#33381](https://github.com/RocketChat/Rocket.Chat/pull/33381)) Fixes a race condition that causes livechat conversations to get stuck in the agent's sidebar panel after being forwarded. + +- ([#33355](https://github.com/RocketChat/Rocket.Chat/pull/33355)) This adjustment removes the deprecated `removeWebdavAccount` method. Moving forward, use the `webdav.removeWebdavAccount` endpoint to remove WebDAV accounts. + +- ([#33527](https://github.com/RocketChat/Rocket.Chat/pull/33527)) Fixes E2EE composer freezing when the room state changes + +- ([#33332](https://github.com/RocketChat/Rocket.Chat/pull/33332)) Fixes an issue where a thread message was not scrolled to view when it was opened using its link. + +- ([#33028](https://github.com/RocketChat/Rocket.Chat/pull/33028)) Fixes an issue where ignore threads parameter were not being affected by retention policy overriding in old channels + +- ([#33346](https://github.com/RocketChat/Rocket.Chat/pull/33346)) Implements integration with FreeSwitch to enable VoIP calls for team collaboration workspaces + +- ([#33364](https://github.com/RocketChat/Rocket.Chat/pull/33364)) Fixed Twilio request validation by accepting URLs with query strings + +- ([#33330](https://github.com/RocketChat/Rocket.Chat/pull/33330)) Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates) + +- ([#33606](https://github.com/RocketChat/Rocket.Chat/pull/33606)) Fixed the regex for domains in the isValidDomain function + +- ([#33375](https://github.com/RocketChat/Rocket.Chat/pull/33375)) Fixes title missing in thread list for threads starting with attachment in E2EE rooms + +- ([#33435](https://github.com/RocketChat/Rocket.Chat/pull/33435)) Fixes an issue causing server to not notify users via websocket of new E2EE keys suggested by other users to them when running in development environments. + +- ([#33412](https://github.com/RocketChat/Rocket.Chat/pull/33412)) Fixes a logical error when updating the `responseBy` property of a room while `waitingResponse` property was still defined. + +- ([#33631](https://github.com/RocketChat/Rocket.Chat/pull/33631)) Fixes E2EE not rendering new navigation feature preview + +- ([#33417](https://github.com/RocketChat/Rocket.Chat/pull/33417)) Fixes issue with previously disabled private apps being auto enabled on update + +- ([#33583](https://github.com/RocketChat/Rocket.Chat/pull/33583)) Security Hotfix (https://docs.rocket.chat/docs/security-fixes-and-updates) + +- ([#33424](https://github.com/RocketChat/Rocket.Chat/pull/33424)) Fixes a problem with custom sounds causing files to not be playable when using filesystem storage. + +- ([#28042](https://github.com/RocketChat/Rocket.Chat/pull/28042)) Changes the default base Docker image to Alpine. Previously we were shipping Alpine as an alternative flavor under the tag rocketchat/rocket.chat:{release}.alpine , we have been testing this for a while now so we're migrating to use the official one to Alpine. + + We'll still ship a Debian variant under the tag rocketchat/rocket.chat:{release}.debian. + +- ([#33390](https://github.com/RocketChat/Rocket.Chat/pull/33390)) Removed deprecated method `livechat:loadHistory` method. Moving forward use the `ivechat/messages.history/:rid` endpoint + +- ([#33358](https://github.com/RocketChat/Rocket.Chat/pull/33358)) This adjustment removes the deprecated `Mailer.sendMail` and `Mailer:unsubscribe` methods. Moving forward, use the `mailer` and `mailer.unsubscribe` endpoints. + +- ([#33405](https://github.com/RocketChat/Rocket.Chat/pull/33405)) Fixes an issue where a user may receive an indefinite amount of "suggested keys" from all the users on a room even when a user already suggested a key for them, duplicating the work on server. + + Now, the endpoint that returns users waiting for keys will return only users that don't have neither `E2EKey` nor `E2ESuggestedKey`. + +- ([#33652](https://github.com/RocketChat/Rocket.Chat/pull/33652)) Fixes message toolbar showing in End-to-end encrypted room when messages are not decrypted + +- ([#33372](https://github.com/RocketChat/Rocket.Chat/pull/33372)) This adjustment removes deprecated `livechat:addAgent` and `livechat:addManager` method. Moving forward use `livechat/users/agent` and `livechat/users/manager` endpoints to add agent and manager respectively. + +- ([#33507](https://github.com/RocketChat/Rocket.Chat/pull/33507)) Fixed an issue where "Filter by room type" was selectable in the Rooms filter. + +- ([#33378](https://github.com/RocketChat/Rocket.Chat/pull/33378)) Fixes async E2EE key exchange in development environments where change streams are no longer used + +-
Updated dependencies [c5b0c98803, 2806cb5d3e, 3395c8290b, d44f614d0c, 34ed9ad646, 6b5b91fd14, 7726d68374, 687f1efd5f, bcacbb1cee, 9274cf4586, 9bcb802fdc, f33c07ebb8, 9cf079721b, b167db0b37, f4365b7dd4, d9fe5bbe0b, b338807d76, 5f9826bed6, debd3ffa22, a5f25e73b5, 3ea02d3cc1, e3629e065b, b19ae4dbc7, 03d148524b, 3f9c3f1f52, 760ae5c01a, 4aa731d6e9, 53cc1111f8, 81998f3450, e3dac4aab6, 50943a02e8, 5acb59bb39, 509143d6dd, 31eb47f573, fa501ecb53]: + + - @rocket.chat/i18n@1.0.0-rc.0 + - @rocket.chat/rest-typings@7.0.0-rc.0 + - @rocket.chat/web-ui-registration@12.0.0-rc.0 + - @rocket.chat/apps@0.2.0-rc.0 + - @rocket.chat/core-typings@7.0.0-rc.0 + - @rocket.chat/model-typings@1.0.0-rc.0 + - @rocket.chat/ui-kit@0.37.0-rc.0 + - @rocket.chat/freeswitch@1.0.0-rc.0 + - @rocket.chat/core-services@0.7.1-rc.0 + - @rocket.chat/ui-client@12.0.0-rc.0 + - @rocket.chat/ui-voip@2.0.0-rc.0 + - @rocket.chat/fuselage-ui-kit@12.0.0-rc.0 + - @rocket.chat/ui-theming@0.4.0-rc.0 + - @rocket.chat/ui-video-conf@12.0.0-rc.0 + - @rocket.chat/ui-composer@0.4.0-rc.0 + - @rocket.chat/gazzodown@12.0.0-rc.0 + - @rocket.chat/ui-avatar@8.0.0-rc.0 + - @rocket.chat/license@1.0.0-rc.0 + - @rocket.chat/models@1.0.0-rc.0 + - @rocket.chat/apps-engine@1.47.0-rc.0 + - @rocket.chat/ui-contexts@12.0.0-rc.0 + - @rocket.chat/omnichannel-services@0.3.6-rc.0 + - @rocket.chat/presence@0.2.9-rc.0 + - @rocket.chat/api-client@0.2.9-rc.0 + - @rocket.chat/pdf-worker@0.2.6-rc.0 + - @rocket.chat/cron@0.1.9-rc.0 + - @rocket.chat/network-broker@0.1.1-rc.0 + - @rocket.chat/server-cloud-communication@0.0.2 + - @rocket.chat/instance-status@0.1.9-rc.0 +
+ ## 6.13.0 ### Minor Changes diff --git a/apps/meteor/app/2fa/server/loginHandler.ts b/apps/meteor/app/2fa/server/loginHandler.ts index 26c9869a132f..1555a7904ae2 100644 --- a/apps/meteor/app/2fa/server/loginHandler.ts +++ b/apps/meteor/app/2fa/server/loginHandler.ts @@ -71,11 +71,11 @@ const recreateError = (errorDoc: Error | Meteor.Error): Error | Meteor.Error => return copyTo(errorDoc, error); }; -OAuth._retrievePendingCredential = function (key, ...args): string | Error | void { +OAuth._retrievePendingCredential = async function (key, ...args): Promise { const credentialSecret = args.length > 0 && args[0] !== undefined ? args[0] : undefined; check(key, String); - const pendingCredential = OAuth._pendingCredentials.findOne({ + const pendingCredential = await OAuth._pendingCredentials.findOneAsync({ key, credentialSecret, }); diff --git a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts index 807f72080e4b..f96e3a46f7fb 100644 --- a/apps/meteor/app/api/server/helpers/parseJsonQuery.ts +++ b/apps/meteor/app/api/server/helpers/parseJsonQuery.ts @@ -107,8 +107,16 @@ export async function parseJsonQuery(api: PartialThis): Promise<{ } } + const allowedRoutes = [ + '/api/v1/settings.public', + '/api/v1/directory', + '/api/v1/channels.messages', + '/api/v1/groups.messages', + '/api/v1/dm.messages', + '/api/v1/im.messages', + ]; let query: Record = {}; - if (params.query && isUnsafeQueryParamsAllowed) { + if (params.query && (isUnsafeQueryParamsAllowed || allowedRoutes.includes(route))) { apiDeprecationLogger.parameter(route, 'query', '8.0.0', response, messageGenerator); try { query = ejson.parse(params.query); diff --git a/apps/meteor/app/apps/server/bridges/email.ts b/apps/meteor/app/apps/server/bridges/email.ts index 4c9cb9a93ed6..6d75a4504483 100644 --- a/apps/meteor/app/apps/server/bridges/email.ts +++ b/apps/meteor/app/apps/server/bridges/email.ts @@ -3,6 +3,7 @@ import type { IEmail } from '@rocket.chat/apps-engine/definition/email'; import { EmailBridge } from '@rocket.chat/apps-engine/server/bridges'; import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; export class AppEmailBridge extends EmailBridge { constructor(private readonly orch: IAppServerOrchestrator) { @@ -10,7 +11,13 @@ export class AppEmailBridge extends EmailBridge { } protected async sendEmail(email: IEmail, appId: string): Promise { + let { from } = email; + if (!from) { + this.orch.debugLog(`The app ${appId} didn't provide a from address, using the default one.`); + from = String(settings.get('From_Email')); + } + this.orch.debugLog(`The app ${appId} is sending an email.`); - await Mailer.send(email); + await Mailer.send({ ...email, from }); } } diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 235c2975ae19..f2521d2f8cf5 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -54,21 +54,6 @@ export class AppLivechatBridge extends LivechatBridge { const appMessage = (await this.orch.getConverters().get('messages').convertAppMessage(message)) as IMessage | undefined; const livechatMessage = appMessage as ILivechatMessage | undefined; - if (guest) { - const visitorSource = { - type: OmnichannelSourceType.APP, - id: appId, - alias: this.orch.getManager()?.getOneById(appId)?.getNameSlug(), - }; - const fullVisitor = await LivechatVisitors.findOneEnabledByIdAndSource({ - _id: guest._id, - sourceFilter: { 'source.type': visitorSource.type, 'source.id': visitorSource.id, 'source.alias': visitorSource.alias }, - }); - if (!fullVisitor?.source) { - await LivechatVisitors.setSourceById(guest._id, visitorSource); - } - } - const msg = await LivechatTyped.sendMessage({ guest: guest as ILivechatVisitor, message: livechatMessage as ILivechatMessage, @@ -303,7 +288,7 @@ export class AppLivechatBridge extends LivechatBridge { } return Promise.all( - (await LivechatVisitors.findEnabledBySource({ 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, query).toArray()).map( + (await LivechatVisitors.findEnabled(query).toArray()).map( async (visitor) => visitor && this.orch.getConverters()?.get('visitors').convertVisitor(visitor), ), ); @@ -312,7 +297,7 @@ export class AppLivechatBridge extends LivechatBridge { protected async findVisitorById(id: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertByIdAndSource(id, appId); + return this.orch.getConverters()?.get('visitors').convertById(id); } protected async findVisitorByEmail(email: string, appId: string): Promise { @@ -321,9 +306,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.findOneGuestByEmailAddressAndSource(email, { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }), - ); + .convertVisitor(await LivechatVisitors.findOneGuestByEmailAddress(email)); } protected async findVisitorByToken(token: string, appId: string): Promise { @@ -332,12 +315,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.getVisitorByTokenAndSource({ - token, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }), - ); + .convertVisitor(await LivechatVisitors.getVisitorByToken(token, {})); } protected async findVisitorByPhoneNumber(phoneNumber: string, appId: string): Promise { @@ -346,12 +324,7 @@ export class AppLivechatBridge extends LivechatBridge { return this.orch .getConverters() ?.get('visitors') - .convertVisitor( - await LivechatVisitors.findOneVisitorByPhoneAndSource(phoneNumber, { - 'source.type': OmnichannelSourceType.APP, - 'source.id': appId, - }), - ); + .convertVisitor(await LivechatVisitors.findOneVisitorByPhone(phoneNumber)); } protected async findDepartmentByIdOrName(value: string, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index 23aa3b3d450c..0761d3c9bae4 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,4 +1,3 @@ -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { transformMappedData } from './transformMappedData'; @@ -15,30 +14,12 @@ export class AppVisitorsConverter { return this.convertVisitor(visitor); } - async convertByIdAndSource(id, appId) { - const visitor = await LivechatVisitors.findOneEnabledByIdAndSource({ - _id: id, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }); - - return this.convertVisitor(visitor); - } - async convertByToken(token) { const visitor = await LivechatVisitors.getVisitorByToken(token); return this.convertVisitor(visitor); } - async convertByTokenAndSource(token, appId) { - const visitor = await LivechatVisitors.getVisitorByTokenAndSource({ - token, - sourceFilter: { 'source.type': OmnichannelSourceType.APP, 'source.id': appId }, - }); - - return this.convertVisitor(visitor); - } - async convertVisitor(visitor) { if (!visitor) { return undefined; diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts index 580356876dad..966b7e6d6af4 100644 --- a/apps/meteor/app/livechat/imports/server/rest/departments.ts +++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts @@ -14,8 +14,13 @@ import { findDepartmentAgents, findArchivedDepartments, } from '../../../server/api/lib/departments'; -import { DepartmentHelper } from '../../../server/lib/Departments'; -import { saveDepartment, archiveDepartment, unarchiveDepartment, saveDepartmentAgents } from '../../../server/lib/departmentsLib'; +import { + saveDepartment, + archiveDepartment, + unarchiveDepartment, + saveDepartmentAgents, + removeDepartment, +} from '../../../server/lib/departmentsLib'; import { isDepartmentCreationAvailable } from '../../../server/lib/isDepartmentCreationAvailable'; API.v1.addRoute( @@ -143,7 +148,7 @@ API.v1.addRoute( _id: String, }); - await DepartmentHelper.removeDepartment(this.urlParams._id); + await removeDepartment(this.urlParams._id); return API.v1.success(); }, diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.ts b/apps/meteor/app/livechat/imports/server/rest/sms.ts index 8c6701ac67eb..2f13fd01f221 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/sms.ts @@ -1,12 +1,11 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; import type { ILivechatVisitor, - IOmnichannelRoom, IUpload, MessageAttachment, ServiceData, FileAttachmentProps, - IOmnichannelSource, + IOmnichannelRoomInfo, } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; @@ -56,24 +55,10 @@ const defineDepartment = async (idOrName?: string) => { return department?._id; }; -const defineVisitor = async (smsNumber: string, serviceName: string, destination: string, targetDepartment?: string) => { - const visitorSource: IOmnichannelSource = { - type: OmnichannelSourceType.SMS, - alias: serviceName, - }; - - const visitor = await LivechatVisitors.findOneVisitorByPhoneAndSource( - smsNumber, - { - 'source.type': visitorSource.type, - 'source.alias': visitorSource.alias, - }, - { projection: { token: 1 } }, - ); - visitorSource.destination = destination; - let data: { token: string; source: IOmnichannelSource; department?: string } = { +const defineVisitor = async (smsNumber: string, targetDepartment?: string) => { + const visitor = await LivechatVisitors.findOneVisitorByPhone(smsNumber); + let data: { token: string; department?: string } = { token: visitor?.token || Random.id(), - source: visitorSource, }; if (!visitor) { @@ -132,15 +117,12 @@ API.v1.addRoute('livechat/sms-incoming/:service', { targetDepartment = await defineDepartment(smsDepartment); } - const visitor = await defineVisitor(sms.from, service, sms.to, targetDepartment); + const visitor = await defineVisitor(sms.from, targetDepartment); if (!visitor) { return API.v1.success(SMSService.error(new Error('Invalid visitor'))); } - const roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - } = { + const roomInfo: IOmnichannelRoomInfo = { sms: { from: sms.to, }, @@ -259,10 +241,7 @@ API.v1.addRoute('livechat/sms-incoming/:service', { const sendMessage: { guest: ILivechatVisitor; message: ILivechatMessage; - roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - }; + roomInfo: IOmnichannelRoomInfo; } = { guest: visitor, roomInfo, diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.ts b/apps/meteor/app/livechat/imports/server/rest/upload.ts index 30bb686fe06b..14db8f20afcf 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.ts +++ b/apps/meteor/app/livechat/imports/server/rest/upload.ts @@ -1,9 +1,7 @@ -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import filesize from 'filesize'; import { API } from '../../../../api/server'; -import { isWidget } from '../../../../api/server/helpers/isWidget'; import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData'; import { FileUpload } from '../../../../file-upload/server'; import { settings } from '../../../../settings/server'; @@ -15,7 +13,6 @@ API.v1.addRoute('livechat/upload/:rid', { if (!this.request.headers['x-visitor-token']) { return API.v1.unauthorized(); } - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; const canUpload = settings.get('Livechat_fileupload_enabled') && settings.get('FileUpload_Enabled'); @@ -26,10 +23,7 @@ API.v1.addRoute('livechat/upload/:rid', { } const visitorToken = this.request.headers['x-visitor-token']; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource({ - token: visitorToken as string, - sourceFilter: { 'source.type': sourceType }, - }); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken as string, {}); if (!visitor) { return API.v1.unauthorized(); @@ -82,10 +76,6 @@ API.v1.addRoute('livechat/upload/:rid', { return API.v1.failure('Invalid file'); } - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, { type: sourceType }); - } - uploadedFile.description = fields.description; delete fields.description; diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.ts b/apps/meteor/app/livechat/server/api/lib/livechat.ts index 397683ef39c7..c3c22c327731 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.ts +++ b/apps/meteor/app/livechat/server/api/lib/livechat.ts @@ -1,11 +1,4 @@ -import type { - ILivechatAgent, - ILivechatDepartment, - ILivechatTrigger, - ILivechatVisitor, - IOmnichannelRoom, - OmnichannelSourceType, -} from '@rocket.chat/core-typings'; +import type { ILivechatAgent, ILivechatDepartment, ILivechatTrigger, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -59,21 +52,6 @@ export function findGuest(token: string): Promise { return LivechatVisitors.getVisitorByToken(token); } -export function findGuestBySource(token: string, sourceType: OmnichannelSourceType): Promise { - const projection = { - name: 1, - username: 1, - token: 1, - visitorEmails: 1, - department: 1, - activity: 1, - contactId: 1, - source: 1, - }; - - return LivechatVisitors.getVisitorByTokenAndSource({ token, sourceFilter: { 'source.type': sourceType } }, { projection }); -} - export function findGuestWithoutActivity(token: string): Promise { return LivechatVisitors.getVisitorByToken(token, { projection: { name: 1, username: 1, token: 1, visitorEmails: 1, department: 1 } }); } diff --git a/apps/meteor/app/livechat/server/api/v1/agent.ts b/apps/meteor/app/livechat/server/api/v1/agent.ts index abc6163fe9c9..6c583e1b24db 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.ts +++ b/apps/meteor/app/livechat/server/api/v1/agent.ts @@ -7,6 +7,7 @@ import { API } from '../../../../api/server'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; import { RoutingManager } from '../../lib/RoutingManager'; +import { getRequiredDepartment } from '../../lib/departmentsLib'; import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat'; API.v1.addRoute('livechat/agent.info/:rid/:token', { @@ -43,7 +44,7 @@ API.v1.addRoute( let { department } = this.queryParams; if (!department) { - const requireDepartment = await LivechatTyped.getRequiredDepartment(); + const requireDepartment = await getRequiredDepartment(); if (requireDepartment) { department = requireDepartment._id; } diff --git a/apps/meteor/app/livechat/server/api/v1/message.ts b/apps/meteor/app/livechat/server/api/v1/message.ts index bde332af5db4..b7eb6e1f684a 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.ts +++ b/apps/meteor/app/livechat/server/api/v1/message.ts @@ -1,4 +1,3 @@ -import type { IOmnichannelSource } from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, Messages } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; @@ -19,7 +18,7 @@ import { loadMessageHistory } from '../../../../lib/server/functions/loadMessage import { settings } from '../../../../settings/server'; import { normalizeMessageFileUpload } from '../../../../utils/server/functions/normalizeMessageFileUpload'; import { Livechat as LivechatTyped } from '../../lib/LivechatTyped'; -import { findGuest, findGuestBySource, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; +import { findGuest, findRoom, normalizeHttpHeaderData } from '../lib/livechat'; API.v1.addRoute( 'livechat/message', @@ -27,9 +26,8 @@ API.v1.addRoute( { async post() { const { token, rid, agent, msg } = this.bodyParams; - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; - const guest = await findGuestBySource(token, sourceType); + const guest = await findGuest(token); if (!guest) { throw new Error('invalid-token'); } @@ -50,10 +48,6 @@ API.v1.addRoute( throw new Error('message-length-exceeds-character-limit'); } - if (!guest.source) { - await LivechatVisitors.setSourceById(guest._id, { type: sourceType }); - } - const _id = this.bodyParams._id || Random.id(); const sendMessage = { @@ -67,7 +61,7 @@ API.v1.addRoute( agent, roomInfo: { source: { - type: sourceType, + type: isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API, }, }, }; @@ -256,12 +250,8 @@ API.v1.addRoute( { async post() { const visitorToken = this.bodyParams.visitor.token; - const sourceType = isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource( - { token: visitorToken, sourceFilter: { 'source.type': sourceType } }, - {}, - ); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken, {}); let rid: string; if (visitor) { const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}); @@ -271,16 +261,11 @@ API.v1.addRoute( } else { rid = Random.id(); } - - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, { type: sourceType }); - } } else { rid = Random.id(); - const guest: typeof this.bodyParams.visitor & { connectionData?: unknown; source?: IOmnichannelSource } = this.bodyParams.visitor; + const guest: typeof this.bodyParams.visitor & { connectionData?: unknown } = this.bodyParams.visitor; guest.connectionData = normalizeHttpHeaderData(this.request.headers); - guest.source = { type: sourceType }; const visitor = await LivechatTyped.registerGuest(guest); if (!visitor) { @@ -305,7 +290,7 @@ API.v1.addRoute( }, roomInfo: { source: { - type: sourceType, + type: isWidget(this.request.headers) ? OmnichannelSourceType.WIDGET : OmnichannelSourceType.API, }, }, }; diff --git a/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts b/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts index a367df7a04ad..f6fb29d17442 100644 --- a/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts +++ b/apps/meteor/app/livechat/server/api/v1/offlineMessage.ts @@ -2,7 +2,7 @@ import { isPOSTLivechatOfflineMessageParams } from '@rocket.chat/rest-typings'; import { i18n } from '../../../../../server/lib/i18n'; import { API } from '../../../../api/server'; -import { Livechat } from '../../lib/LivechatTyped'; +import { sendOfflineMessage } from '../../lib/messages'; API.v1.addRoute( 'livechat/offline.message', @@ -14,7 +14,7 @@ API.v1.addRoute( async post() { const { name, email, message, department, host } = this.bodyParams; try { - await Livechat.sendOfflineMessage({ name, email, message, department, host }); + await sendOfflineMessage({ name, email, message, department, host }); return API.v1.success({ message: i18n.t('Livechat_offline_message_sent') }); } catch (e) { return API.v1.failure(i18n.t('Error_sending_livechat_offline_message')); diff --git a/apps/meteor/app/livechat/server/api/v1/pageVisited.ts b/apps/meteor/app/livechat/server/api/v1/pageVisited.ts index 2688ad673af0..a97b5278462d 100644 --- a/apps/meteor/app/livechat/server/api/v1/pageVisited.ts +++ b/apps/meteor/app/livechat/server/api/v1/pageVisited.ts @@ -2,7 +2,7 @@ import type { IOmnichannelSystemMessage } from '@rocket.chat/core-typings'; import { isPOSTLivechatPageVisitedParams } from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; -import { Livechat } from '../../lib/LivechatTyped'; +import { savePageHistory } from '../../lib/tracking'; API.v1.addRoute( 'livechat/page.visited', @@ -11,7 +11,7 @@ API.v1.addRoute( async post() { const { token, rid, pageInfo } = this.bodyParams; - const message = await Livechat.savePageHistory(token, rid, pageInfo); + const message = await savePageHistory(token, rid, pageInfo); if (!message) { return API.v1.success(); } diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index 5d327e3d4ad7..b46a8c3e0663 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -1,5 +1,5 @@ import { Omnichannel } from '@rocket.chat/core-services'; -import type { ILivechatAgent, IUser, SelectedAgent, TransferByData } from '@rocket.chat/core-typings'; +import type { ILivechatAgent, IOmnichannelInquiryExtraData, IUser, SelectedAgent, TransferByData } from '@rocket.chat/core-typings'; import { isOmnichannelRoom, OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, Users, LivechatRooms, Messages } from '@rocket.chat/models'; import { @@ -80,7 +80,12 @@ API.v1.addRoute( }, }; - const newRoom = await LivechatTyped.createRoom({ visitor: guest, roomInfo, agent, extraData: extraParams }); + const newRoom = await LivechatTyped.createRoom({ + visitor: guest, + roomInfo, + agent, + extraData: extraParams as IOmnichannelInquiryExtraData, + }); return API.v1.success({ room: newRoom, diff --git a/apps/meteor/app/livechat/server/lib/Departments.ts b/apps/meteor/app/livechat/server/lib/Departments.ts deleted file mode 100644 index 3dfa01e4f6b6..000000000000 --- a/apps/meteor/app/livechat/server/lib/Departments.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { ILivechatDepartment } from '@rocket.chat/core-typings'; -import { Logger } from '@rocket.chat/logger'; -import { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms } from '@rocket.chat/models'; - -import { callbacks } from '../../../../lib/callbacks'; -import { notifyOnLivechatDepartmentAgentChanged } from '../../../lib/server/lib/notifyListener'; - -class DepartmentHelperClass { - logger = new Logger('Omnichannel:DepartmentHelper'); - - async removeDepartment(departmentId: string) { - this.logger.debug(`Removing department: ${departmentId}`); - - const department = await LivechatDepartment.findOneById>(departmentId, { - projection: { _id: 1, businessHourId: 1 }, - }); - if (!department) { - throw new Error('error-department-not-found'); - } - - const { _id } = department; - - const ret = await LivechatDepartment.removeById(_id); - if (ret.acknowledged !== true) { - throw new Error('error-failed-to-delete-department'); - } - - const removedAgents = await LivechatDepartmentAgents.findByDepartmentId(department._id, { projection: { agentId: 1 } }).toArray(); - - this.logger.debug( - `Performing post-department-removal actions: ${_id}. Removing department agents, unsetting fallback department and removing department from rooms`, - ); - - const removeByDept = LivechatDepartmentAgents.removeByDepartmentId(_id); - - const promiseResponses = await Promise.allSettled([ - removeByDept, - LivechatDepartment.unsetFallbackDepartmentByDepartmentId(_id), - LivechatRooms.bulkRemoveDepartmentAndUnitsFromRooms(_id), - ]); - - promiseResponses.forEach((response, index) => { - if (response.status === 'rejected') { - this.logger.error(`Error while performing post-department-removal actions: ${_id}. Action No: ${index}. Error:`, response.reason); - } - }); - - const { deletedCount } = await removeByDept; - - if (deletedCount > 0) { - removedAgents.forEach(({ _id: docId, agentId }) => { - void notifyOnLivechatDepartmentAgentChanged( - { - _id: docId, - agentId, - departmentId: _id, - }, - 'removed', - ); - }); - } - - await callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds: removedAgents.map(({ agentId }) => agentId) }); - - return ret; - } -} - -export const DepartmentHelper = new DepartmentHelperClass(); diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 484fd82d16b0..50f8798f1c0a 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -14,6 +14,9 @@ import type { ILivechatAgent, ILivechatDepartment, IOmnichannelSource, + IOmnichannelRoomInfo, + IOmnichannelInquiryExtraData, + IOmnichannelRoomExtraData, } from '@rocket.chat/core-typings'; import { LivechatInquiryStatus, OmnichannelSourceType, DEFAULT_SLA_CONFIG, UserStatus } from '@rocket.chat/core-typings'; import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority'; @@ -63,18 +66,12 @@ export const allowAgentSkipQueue = (agent: SelectedAgent) => { return hasRoleAsync(agent.agentId, 'bot'); }; -export const createLivechatRoom = async < - E extends Record & { - sla?: string; - customFields?: Record; - source?: OmnichannelSourceType; - }, ->( +export const createLivechatRoom = async ( rid: string, name: string, guest: ILivechatVisitor, - roomInfo: Partial = {}, - extraData?: E, + roomInfo: IOmnichannelRoomInfo = {}, + extraData?: IOmnichannelRoomExtraData, ) => { check(rid, String); check(name, String); @@ -171,7 +168,7 @@ export const createLivechatInquiry = async ({ guest?: Pick; message?: string; initialStatus?: LivechatInquiryStatus; - extraData?: Pick; + extraData?: IOmnichannelInquiryExtraData; }) => { check(rid, String); check(name, String); diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 4a087eba4350..a6fca30675bc 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1,6 +1,3 @@ -import dns from 'dns'; -import * as util from 'util'; - import { Apps, AppEvents } from '@rocket.chat/apps'; import { Message, VideoConf, api, Omnichannel } from '@rocket.chat/core-services'; import type { @@ -16,8 +13,11 @@ import type { TransferData, IOmnichannelAgent, ILivechatInquiryRecord, - OmnichannelSourceType, UserStatus, + ILivechatContact, + ILivechatContactChannel, + IOmnichannelRoomInfo, + IOmnichannelRoomExtraData, } from '@rocket.chat/core-typings'; import { ILivechatAgentStatus, isOmnichannelRoom } from '@rocket.chat/core-typings'; import { Logger, type MainLogger } from '@rocket.chat/logger'; @@ -63,7 +63,6 @@ import { notifyOnSubscriptionChangedByRoomId, notifyOnSubscriptionChanged, } from '../../../lib/server/lib/notifyListener'; -import * as Mailer from '../../../mailer/server/api'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; import { businessHourManager } from '../business-hour'; @@ -72,6 +71,7 @@ import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; import { Visitors } from './Visitors'; import { registerGuestData } from './contacts/registerGuestData'; +import { getRequiredDepartment } from './departmentsLib'; import type { CloseRoomParams, CloseRoomParamsByUser, CloseRoomParamsByVisitor, ILivechatMessage } from './localTypes'; import { parseTranscriptRequest } from './parseTranscriptRequest'; @@ -82,20 +82,10 @@ export type RegisterGuestType = Partial = { [K in keyof T]?: T[K]; }; -type PageInfo = { title: string; location: { href: string }; change: string }; - type ICRMData = { _id: string; label?: string; @@ -123,8 +113,6 @@ const isRoomClosedByUserParams = (params: CloseRoomParams): params is CloseRoomP const isRoomClosedByVisitorParams = (params: CloseRoomParams): params is CloseRoomParamsByVisitor => (params as CloseRoomParamsByVisitor).visitor !== undefined; -const dnsResolveMx = util.promisify(dns.resolveMx); - class LivechatClass { logger: Logger; @@ -346,24 +334,6 @@ class LivechatClass { return { room: newRoom, closedBy: closeData.closedBy, removedInquiry: inquiry }; } - async getRequiredDepartment(onlineRequired = true) { - const departments = LivechatDepartment.findEnabledWithAgents(); - - for await (const dept of departments) { - if (!dept.showOnRegistration) { - continue; - } - if (!onlineRequired) { - return dept; - } - - const onlineAgents = await LivechatDepartmentAgents.getOnlineForDepartment(dept._id); - if (onlineAgents && (await onlineAgents.count())) { - return dept; - } - } - } - async createRoom({ visitor, message, @@ -375,12 +345,9 @@ class LivechatClass { visitor: ILivechatVisitor; message?: string; rid?: string; - roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - }; + roomInfo: IOmnichannelRoomInfo; agent?: SelectedAgent; - extraData?: Record; + extraData?: IOmnichannelRoomExtraData; }) { if (!settings.get('Livechat_enabled')) { throw new Meteor.Error('error-omnichannel-is-disabled'); @@ -389,7 +356,7 @@ class LivechatClass { const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, visitor); // if no department selected verify if there is at least one active and pick the first if (!defaultAgent && !visitor.department) { - const department = await this.getRequiredDepartment(); + const department = await getRequiredDepartment(); Livechat.logger.debug(`No department or default agent selected for ${visitor._id}`); if (department) { @@ -421,21 +388,12 @@ class LivechatClass { return room; } - async getRoom< - E extends Record & { - sla?: string; - customFields?: Record; - source?: OmnichannelSourceType; - }, - >( + async getRoom( guest: ILivechatVisitor, message: Pick, - roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - }, + roomInfo: IOmnichannelRoomInfo, agent?: SelectedAgent, - extraData?: E, + extraData?: IOmnichannelRoomExtraData, ) { if (!settings.get('Livechat_enabled')) { throw new Meteor.Error('error-omnichannel-is-disabled'); @@ -490,30 +448,6 @@ class LivechatClass { return Users.checkOnlineAgents(); } - async setDepartmentForGuest({ token, department }: { token: string; department: string }) { - check(token, String); - check(department, String); - - Livechat.logger.debug(`Switching departments for user with token ${token} (to ${department})`); - - const updateUser = { - $set: { - department, - }, - }; - - const dep = await LivechatDepartment.findOneById>(department, { projection: { _id: 1 } }); - if (!dep) { - throw new Meteor.Error('invalid-department', 'Provided department does not exists'); - } - - const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); - if (!visitor) { - throw new Meteor.Error('invalid-token', 'Provided token is invalid'); - } - await LivechatVisitors.updateById(visitor._id, updateUser); - } - async removeRoom(rid: string) { Livechat.logger.debug(`Deleting room ${rid}`); check(rid, String); @@ -634,16 +568,6 @@ class LivechatClass { }; } - private async sendEmail(from: string, to: string, replyTo: string, subject: string, html: string): Promise { - await Mailer.send({ - to, - from, - replyTo, - subject, - html, - }); - } - async sendRequest( postData: { type: string; @@ -917,69 +841,6 @@ class LivechatClass { return rcSettings; } - async sendOfflineMessage(data: OfflineMessageData) { - if (!settings.get('Livechat_display_offline_form')) { - throw new Error('error-offline-form-disabled'); - } - - const { message, name, email, department, host } = data; - - if (!email) { - throw new Error('error-invalid-email'); - } - - const emailMessage = `${message}`.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2'); - - let html = '

New livechat message

'; - if (host && host !== '') { - html = html.concat(`

Sent from: ${host}

`); - } - html = html.concat(` -

Visitor name: ${name}

-

Visitor email: ${email}

-

Message:
${emailMessage}

`); - - const fromEmail = settings.get('From_Email').match(/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i); - - let from: string; - if (fromEmail) { - from = fromEmail[0]; - } else { - from = settings.get('From_Email'); - } - - if (settings.get('Livechat_validate_offline_email')) { - const emailDomain = email.substr(email.lastIndexOf('@') + 1); - - try { - await dnsResolveMx(emailDomain); - } catch (e) { - throw new Meteor.Error('error-invalid-email-address'); - } - } - - // TODO Block offline form if Livechat_offline_email is undefined - // (it does not make sense to have an offline form that does nothing) - // `this.sendEmail` will throw an error if the email is invalid - // thus this breaks livechat, since the "to" email is invalid, and that returns an [invalid email] error to the livechat client - let emailTo = settings.get('Livechat_offline_email'); - if (department && department !== '') { - const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { email: 1 } }); - if (dep) { - emailTo = dep.email || emailTo; - } - } - - const fromText = `${name} - ${email} <${from}>`; - const replyTo = `${name} <${email}>`; - const subject = `Livechat offline message from ${name}: ${`${emailMessage}`.substring(0, 20)}`; - await this.sendEmail(fromText, emailTo, replyTo, subject, html); - - setImmediate(() => { - void callbacks.run('livechat.offlineMessage', data); - }); - } - async sendMessage({ guest, message, @@ -988,10 +849,7 @@ class LivechatClass { }: { guest: ILivechatVisitor; message: ILivechatMessage; - roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - }; + roomInfo: IOmnichannelRoomInfo; agent?: SelectedAgent; }) { const { room, newRoom } = await this.getRoom(guest, message, roomInfo, agent); @@ -1276,53 +1134,6 @@ class LivechatClass { return true; } - async savePageHistory(token: string, roomId: string | undefined, pageInfo: PageInfo) { - this.logger.debug({ - msg: `Saving page movement history for visitor with token ${token}`, - pageInfo, - roomId, - }); - - if (pageInfo.change !== settings.get('Livechat_history_monitor_type')) { - return; - } - const user = await Users.findOneById('rocket.cat'); - - if (!user) { - throw new Error('error-invalid-user'); - } - - const pageTitle = pageInfo.title; - const pageUrl = pageInfo.location.href; - const extraData: { - navigation: { - page: PageInfo; - token: string; - }; - expireAt?: number; - _hidden?: boolean; - } = { - navigation: { - page: pageInfo, - token, - }, - }; - - if (!roomId) { - this.logger.warn(`Saving page history without room id for visitor with token ${token}`); - // keep history of unregistered visitors for 1 month - const keepHistoryMiliseconds = 2592000000; - extraData.expireAt = new Date().getTime() + keepHistoryMiliseconds; - } - - if (!settings.get('Livechat_Visitor_navigation_as_a_message')) { - extraData._hidden = true; - } - - // @ts-expect-error: Investigating on which case we won't receive a roomId and where that history is supposed to be stored - return Message.saveSystemMessage('livechat_navigation_history', roomId, `${pageTitle} - ${pageUrl}`, user, extraData); - } - async afterRemoveAgent(user: AtLeast) { await callbacks.run('livechat.afterAgentRemoved', { agent: user }); return true; diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index 608c2adbdc71..213131bf0544 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -1,13 +1,12 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import { Omnichannel } from '@rocket.chat/core-services'; -import type { ILivechatDepartment } from '@rocket.chat/core-typings'; +import type { ILivechatDepartment, IOmnichannelRoomInfo, IOmnichannelRoomExtraData } from '@rocket.chat/core-typings'; import { LivechatInquiryStatus, type ILivechatInquiryRecord, type ILivechatVisitor, type IOmnichannelRoom, type SelectedAgent, - type OmnichannelSourceType, } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { LivechatContacts, LivechatDepartment, LivechatDepartmentAgents, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; @@ -147,29 +146,20 @@ export class QueueManager { await this.dispatchInquiryQueued(inquiry, room, defaultAgent); } - static async requestRoom< - E extends Record & { - sla?: string; - customFields?: Record; - source?: OmnichannelSourceType; - }, - >({ + static async requestRoom({ guest, rid = Random.id(), message, roomInfo, agent, - extraData: { customFields, ...extraData } = {} as E, + extraData: { customFields, ...extraData } = {}, }: { guest: ILivechatVisitor; rid?: string; message?: string; - roomInfo: { - source?: IOmnichannelRoom['source']; - [key: string]: unknown; - }; + roomInfo: IOmnichannelRoomInfo; agent?: SelectedAgent; - extraData?: E; + extraData?: IOmnichannelRoomExtraData; }) { logger.debug(`Requesting a room for guest ${guest._id}`); check( @@ -224,7 +214,7 @@ export class QueueManager { } } - const name = (roomInfo?.fname as string) || guest.name || guest.username; + const name = guest.name || guest.username; const room = await createLivechatRoom(rid, name, { ...guest, ...(department && { department }) }, roomInfo, { ...extraData, diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index 28e5c72efc16..8781f675ebf9 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -1,5 +1,5 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; -import { Message, Omnichannel } from '@rocket.chat/core-services'; +import { Message } from '@rocket.chat/core-services'; import type { ILivechatInquiryRecord, ILivechatVisitor, @@ -12,7 +12,6 @@ import type { TransferData, } from '@rocket.chat/core-typings'; import { LivechatInquiryStatus } from '@rocket.chat/core-typings'; -import { License } from '@rocket.chat/license'; import { Logger } from '@rocket.chat/logger'; import { LivechatInquiry, LivechatRooms, Subscriptions, Rooms, Users } from '@rocket.chat/models'; import { Match, check } from 'meteor/check'; @@ -36,7 +35,6 @@ const logger = new Logger('RoutingManager'); type Routing = { methods: Record; - startQueue(): Promise; isMethodSet(): boolean; registerMethod(name: string, Method: IRoutingMethodConstructor): void; getMethod(): IRoutingMethod; @@ -68,16 +66,6 @@ type Routing = { export const RoutingManager: Routing = { methods: {}, - async startQueue() { - const shouldPreventQueueStart = await License.shouldPreventAction('monthlyActiveContacts'); - - if (shouldPreventQueueStart) { - logger.error('Monthly Active Contacts limit reached. Queue will not start'); - return; - } - void (await Omnichannel.getQueueWorker()).shouldStart(); - }, - isMethodSet() { return settings.get('Livechat_Routing_Method') !== ''; }, diff --git a/apps/meteor/app/livechat/server/lib/departmentsLib.ts b/apps/meteor/app/livechat/server/lib/departmentsLib.ts index 79b8d40ed387..f149078788e3 100644 --- a/apps/meteor/app/livechat/server/lib/departmentsLib.ts +++ b/apps/meteor/app/livechat/server/lib/departmentsLib.ts @@ -1,11 +1,15 @@ import type { LivechatDepartmentDTO, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; -import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; +import { LivechatDepartment, LivechatDepartmentAgents, LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../lib/callbacks'; -import { notifyOnLivechatDepartmentAgentChangedByDepartmentId } from '../../../lib/server/lib/notifyListener'; +import { + notifyOnLivechatDepartmentAgentChangedByDepartmentId, + notifyOnLivechatDepartmentAgentChanged, +} from '../../../lib/server/lib/notifyListener'; import { updateDepartmentAgents } from './Helper'; import { isDepartmentCreationAvailable } from './isDepartmentCreationAvailable'; +import { livechatLogger } from './logger'; /** * @param {string|null} _id - The department id * @param {Partial} departmentData @@ -199,3 +203,102 @@ export async function saveDepartmentAgents( return updateDepartmentAgents(_id, departmentAgents, department.enabled); } + +export async function setDepartmentForGuest({ token, department }: { token: string; department: string }) { + check(token, String); + check(department, String); + + livechatLogger.debug(`Switching departments for user with token ${token} (to ${department})`); + + const updateUser = { + $set: { + department, + }, + }; + + const dep = await LivechatDepartment.findOneById>(department, { projection: { _id: 1 } }); + if (!dep) { + throw new Meteor.Error('invalid-department', 'Provided department does not exists'); + } + + const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); + if (!visitor) { + throw new Meteor.Error('invalid-token', 'Provided token is invalid'); + } + await LivechatVisitors.updateById(visitor._id, updateUser); +} + +export async function removeDepartment(departmentId: string) { + livechatLogger.debug(`Removing department: ${departmentId}`); + + const department = await LivechatDepartment.findOneById>(departmentId, { + projection: { _id: 1, businessHourId: 1 }, + }); + if (!department) { + throw new Error('error-department-not-found'); + } + + const { _id } = department; + + const ret = await LivechatDepartment.removeById(_id); + if (ret.acknowledged !== true) { + throw new Error('error-failed-to-delete-department'); + } + + const removedAgents = await LivechatDepartmentAgents.findByDepartmentId(department._id, { projection: { agentId: 1 } }).toArray(); + + livechatLogger.debug( + `Performing post-department-removal actions: ${_id}. Removing department agents, unsetting fallback department and removing department from rooms`, + ); + + const removeByDept = LivechatDepartmentAgents.removeByDepartmentId(_id); + + const promiseResponses = await Promise.allSettled([ + removeByDept, + LivechatDepartment.unsetFallbackDepartmentByDepartmentId(_id), + LivechatRooms.bulkRemoveDepartmentAndUnitsFromRooms(_id), + ]); + + promiseResponses.forEach((response, index) => { + if (response.status === 'rejected') { + livechatLogger.error(`Error while performing post-department-removal actions: ${_id}. Action No: ${index}. Error:`, response.reason); + } + }); + + const { deletedCount } = await removeByDept; + + if (deletedCount > 0) { + removedAgents.forEach(({ _id: docId, agentId }) => { + void notifyOnLivechatDepartmentAgentChanged( + { + _id: docId, + agentId, + departmentId: _id, + }, + 'removed', + ); + }); + } + + await callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds: removedAgents.map(({ agentId }) => agentId) }); + + return ret; +} + +export async function getRequiredDepartment(onlineRequired = true) { + const departments = LivechatDepartment.findEnabledWithAgents(); + + for await (const dept of departments) { + if (!dept.showOnRegistration) { + continue; + } + if (!onlineRequired) { + return dept; + } + + const onlineAgents = await LivechatDepartmentAgents.getOnlineForDepartment(dept._id); + if (onlineAgents && (await onlineAgents.count())) { + return dept; + } + } +} diff --git a/apps/meteor/app/livechat/server/lib/logger.ts b/apps/meteor/app/livechat/server/lib/logger.ts index bb452cd4db04..1e4781240c0c 100644 --- a/apps/meteor/app/livechat/server/lib/logger.ts +++ b/apps/meteor/app/livechat/server/lib/logger.ts @@ -2,3 +2,4 @@ import { Logger } from '@rocket.chat/logger'; export const callbackLogger = new Logger('[Omnichannel] Callback'); export const businessHourLogger = new Logger('Business Hour'); +export const livechatLogger = new Logger('Livechat'); diff --git a/apps/meteor/app/livechat/server/lib/messages.ts b/apps/meteor/app/livechat/server/lib/messages.ts new file mode 100644 index 000000000000..0f5c460e3c28 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/messages.ts @@ -0,0 +1,91 @@ +import dns from 'dns'; +import * as util from 'util'; + +import { LivechatDepartment } from '@rocket.chat/models'; + +import { callbacks } from '../../../../lib/callbacks'; +import * as Mailer from '../../../mailer/server/api'; +import { settings } from '../../../settings/server'; + +const dnsResolveMx = util.promisify(dns.resolveMx); + +type OfflineMessageData = { + message: string; + name: string; + email: string; + department?: string; + host?: string; +}; + +export async function sendOfflineMessage(data: OfflineMessageData) { + if (!settings.get('Livechat_display_offline_form')) { + throw new Error('error-offline-form-disabled'); + } + + const { message, name, email, department, host } = data; + + if (!email) { + throw new Error('error-invalid-email'); + } + + const emailMessage = `${message}`.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2'); + + let html = '

New livechat message

'; + if (host && host !== '') { + html = html.concat(`

Sent from: ${host}

`); + } + html = html.concat(` +

Visitor name: ${name}

+

Visitor email: ${email}

+

Message:
${emailMessage}

`); + + const fromEmail = settings.get('From_Email').match(/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i); + + let from: string; + if (fromEmail) { + from = fromEmail[0]; + } else { + from = settings.get('From_Email'); + } + + if (settings.get('Livechat_validate_offline_email')) { + const emailDomain = email.substr(email.lastIndexOf('@') + 1); + + try { + await dnsResolveMx(emailDomain); + } catch (e) { + throw new Meteor.Error('error-invalid-email-address'); + } + } + + // TODO Block offline form if Livechat_offline_email is undefined + // (it does not make sense to have an offline form that does nothing) + // `this.sendEmail` will throw an error if the email is invalid + // thus this breaks livechat, since the "to" email is invalid, and that returns an [invalid email] error to the livechat client + let emailTo = settings.get('Livechat_offline_email'); + if (department && department !== '') { + const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { email: 1 } }); + if (dep) { + emailTo = dep.email || emailTo; + } + } + + const fromText = `${name} - ${email} <${from}>`; + const replyTo = `${name} <${email}>`; + const subject = `Livechat offline message from ${name}: ${`${emailMessage}`.substring(0, 20)}`; + await sendEmail(fromText, emailTo, replyTo, subject, html); + + setImmediate(() => { + void callbacks.run('livechat.offlineMessage', data); + }); +} + +async function sendEmail(from: string, to: string, replyTo: string, subject: string, html: string): Promise { + await Mailer.send({ + to, + from, + replyTo, + subject, + html, + }); +} diff --git a/apps/meteor/app/livechat/server/lib/tracking.ts b/apps/meteor/app/livechat/server/lib/tracking.ts new file mode 100644 index 000000000000..fb0e91a33668 --- /dev/null +++ b/apps/meteor/app/livechat/server/lib/tracking.ts @@ -0,0 +1,54 @@ +import { Message } from '@rocket.chat/core-services'; +import { Users } from '@rocket.chat/models'; + +import { settings } from '../../../settings/server'; +import { livechatLogger } from './logger'; + +type PageInfo = { title: string; location: { href: string }; change: string }; + +export async function savePageHistory(token: string, roomId: string | undefined, pageInfo: PageInfo) { + livechatLogger.debug({ + msg: `Saving page movement history for visitor with token ${token}`, + pageInfo, + roomId, + }); + + if (pageInfo.change !== settings.get('Livechat_history_monitor_type')) { + return; + } + const user = await Users.findOneById('rocket.cat'); + + if (!user) { + throw new Error('error-invalid-user'); + } + + const pageTitle = pageInfo.title; + const pageUrl = pageInfo.location.href; + const extraData: { + navigation: { + page: PageInfo; + token: string; + }; + expireAt?: number; + _hidden?: boolean; + } = { + navigation: { + page: pageInfo, + token, + }, + }; + + if (!roomId) { + livechatLogger.warn(`Saving page history without room id for visitor with token ${token}`); + // keep history of unregistered visitors for 1 month + const keepHistoryMiliseconds = 2592000000; + extraData.expireAt = new Date().getTime() + keepHistoryMiliseconds; + } + + if (!settings.get('Livechat_Visitor_navigation_as_a_message')) { + extraData._hidden = true; + } + + // @ts-expect-error: Investigating on which case we won't receive a roomId and where that history is supposed to be stored + return Message.saveSystemMessage('livechat_navigation_history', roomId, `${pageTitle} - ${pageUrl}`, user, extraData); +} diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts index 2efecb711b85..742631dcea90 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.ts @@ -42,27 +42,19 @@ export const sendMessageLivechat = async ({ }), ); - const guest = await LivechatVisitors.getVisitorByTokenAndSource( - { token, sourceFilter: { 'source.type': { $in: [OmnichannelSourceType.API, OmnichannelSourceType.WIDGET] } } }, - { - projection: { - name: 1, - username: 1, - department: 1, - token: 1, - source: 1, - }, + const guest = await LivechatVisitors.getVisitorByToken(token, { + projection: { + name: 1, + username: 1, + department: 1, + token: 1, }, - ); + }); if (!guest) { throw new Meteor.Error('invalid-token'); } - if (!guest.source) { - await LivechatVisitors.setSourceById(guest._id, { type: OmnichannelSourceType.API }); - } - if (settings.get('Livechat_enable_message_character_limit') && msg.length > parseInt(settings.get('Livechat_message_character_limit'))) { throw new Meteor.Error('message-length-exceeds-character-limit'); } diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 57013508673e..c7f88646158b 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -1,6 +1,5 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; -import type { IOmnichannelSource } from '@rocket.chat/core-typings'; -import { isEditedMessage, OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../lib/callbacks'; @@ -56,20 +55,11 @@ callbacks.add( return message; } - const visitorSource: IOmnichannelSource = { type: OmnichannelSourceType.SMS, alias: service }; - const visitor = await LivechatVisitors.getVisitorByTokenAndSource( - { token: room.v.token, sourceFilter: { 'source.type': visitorSource.type, 'source.alias': visitorSource.alias } }, - { projection: { phone: 1, source: 1 } }, - ); + const visitor = await LivechatVisitors.getVisitorByToken(room.v.token, { projection: { phone: 1 } }); if (!visitor?.phone || visitor.phone.length === 0) { return message; } - visitorSource.destination = visitor.phone[0].phoneNumber; - - if (!visitor.source) { - await LivechatVisitors.setSourceById(visitor._id, visitorSource); - } try { await SMSService.send(room.sms.from, visitor.phone[0].phoneNumber, message.msg, extraData); diff --git a/apps/meteor/app/livechat/server/startup.ts b/apps/meteor/app/livechat/server/startup.ts index 32cf01d2e640..994dcd64f52a 100644 --- a/apps/meteor/app/livechat/server/startup.ts +++ b/apps/meteor/app/livechat/server/startup.ts @@ -15,7 +15,6 @@ import { settings } from '../../settings/server'; import { businessHourManager } from './business-hour'; import { createDefaultBusinessHourIfNotExists } from './business-hour/Helper'; import { Livechat as LivechatTyped } from './lib/LivechatTyped'; -import { RoutingManager } from './lib/RoutingManager'; import { LivechatAgentActivityMonitor } from './statistics/LivechatAgentActivityMonitor'; import './roomAccessValidator.internalService'; @@ -82,10 +81,6 @@ Meteor.startup(async () => { : undefined, ); - settings.watch('Livechat_Routing_Method', () => { - void RoutingManager.startQueue(); - }); - // Remove when accounts.onLogout is async Accounts.onLogout(({ user }: { user?: IUser }) => { if (!user?.roles?.includes('livechat-agent') || user?.roles?.includes('bot')) { diff --git a/apps/meteor/app/utils/lib/i18n.ts b/apps/meteor/app/utils/lib/i18n.ts index b69fe6b30513..57cc34225d8a 100644 --- a/apps/meteor/app/utils/lib/i18n.ts +++ b/apps/meteor/app/utils/lib/i18n.ts @@ -1,9 +1,18 @@ import type { RocketchatI18nKeys } from '@rocket.chat/i18n'; +import type { TOptions } from 'i18next'; import i18next from 'i18next'; import sprintf from 'i18next-sprintf-postprocessor'; import { isObject } from '../../../lib/utils/isObject'; +declare module 'i18next' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface TFunction { + (key: RocketchatI18nKeys): string; + (key: RocketchatI18nKeys, options: TOptions): string; + } +} + export const i18n = i18next.use(sprintf); export const addSprinfToI18n = function (t: (typeof i18n)['t']) { diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 34642c087e2e..79b54edc4aad 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "7.0.0-develop" + "version": "7.1.0-develop" } diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelToolbar/NavBarItemOmnichannelLivechatToggle.tsx b/apps/meteor/client/NavBarV2/NavBarOmnichannelToolbar/NavBarItemOmnichannelLivechatToggle.tsx index 5bf174362e19..abf8ca432c28 100644 --- a/apps/meteor/client/NavBarV2/NavBarOmnichannelToolbar/NavBarItemOmnichannelLivechatToggle.tsx +++ b/apps/meteor/client/NavBarV2/NavBarOmnichannelToolbar/NavBarItemOmnichannelLivechatToggle.tsx @@ -1,15 +1,16 @@ import { Sidebar } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; -import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useOmnichannelAgentAvailable } from '../../hooks/omnichannel/useOmnichannelAgentAvailable'; type NavBarItemOmnichannelLivechatToggleProps = Omit, 'icon'>; const NavBarItemOmnichannelLivechatToggle = (props: NavBarItemOmnichannelLivechatToggleProps): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const agentAvailable = useOmnichannelAgentAvailable(); const changeAgentStatus = useEndpoint('POST', '/v1/livechat/agent.status'); const dispatchToastMessage = useToastMessageDispatch(); diff --git a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemAuditMenu.tsx b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemAuditMenu.tsx index 7c8a50338e7d..7f47611f9bdb 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemAuditMenu.tsx +++ b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemAuditMenu.tsx @@ -1,15 +1,16 @@ import { NavBarItem } from '@rocket.chat/fuselage'; import { GenericMenu } from '@rocket.chat/ui-client'; -import { useCurrentRoutePath, useTranslation } from '@rocket.chat/ui-contexts'; +import { useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useAuditMenu } from './hooks/useAuditMenu'; type NavBarItemAuditMenuProps = Omit, 'is'>; const NavBarItemAuditMenu = (props: NavBarItemAuditMenuProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const sections = useAuditMenu(); const currentRoute = useCurrentRoutePath(); diff --git a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemMarketPlaceMenu.tsx b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemMarketPlaceMenu.tsx index 85687bb12a2e..1e7fbdefb083 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemMarketPlaceMenu.tsx +++ b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/NavBarItemMarketPlaceMenu.tsx @@ -1,15 +1,16 @@ import { NavBarItem } from '@rocket.chat/fuselage'; import { GenericMenu } from '@rocket.chat/ui-client'; -import { useCurrentRoutePath, useTranslation } from '@rocket.chat/ui-contexts'; +import { useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useMarketPlaceMenu } from './hooks/useMarketPlaceMenu'; type NavBarItemMarketPlaceMenuProps = Omit, 'is'>; const NavBarItemMarketPlaceMenu = (props: NavBarItemMarketPlaceMenuProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const sections = useMarketPlaceMenu(); const currentRoute = useCurrentRoutePath(); diff --git a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/hooks/useAuditMenu.tsx b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/hooks/useAuditMenu.tsx index 97c8d7299497..7c0c36dc9f24 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesToolbar/hooks/useAuditMenu.tsx +++ b/apps/meteor/client/NavBarV2/NavBarPagesToolbar/hooks/useAuditMenu.tsx @@ -1,11 +1,12 @@ import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; -import { usePermission, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouter } from '@rocket.chat/ui-contexts'; +import { useTranslation } from 'react-i18next'; import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; export const useAuditMenu = () => { const router = useRouter(); - const t = useTranslation(); + const { t } = useTranslation(); const hasAuditLicense = useHasLicenseModule('auditing') === true; diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx index 8236eec030e8..a17061050ce9 100644 --- a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx +++ b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx @@ -1,15 +1,16 @@ import { NavBarItem } from '@rocket.chat/fuselage'; import { GenericMenu } from '@rocket.chat/ui-client'; -import { useCurrentRoutePath, useTranslation } from '@rocket.chat/ui-contexts'; +import { useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useAdministrationMenu } from './hooks/useAdministrationMenu'; type NavBarItemAdministrationMenuProps = Omit, 'is'>; const NavBarItemAdministrationMenu = (props: NavBarItemAdministrationMenuProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const currentRoute = useCurrentRoutePath(); const sections = useAdministrationMenu(); diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx index a02c17db0b9b..1ef6f298fccd 100644 --- a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx +++ b/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx @@ -1,13 +1,14 @@ import { Button } from '@rocket.chat/fuselage'; -import { useSessionDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSessionDispatch } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; type NavBarItemLoginPageProps = Omit, 'is'>; const NavBarItemLoginPage = (props: NavBarItemLoginPageProps) => { const setForceLogin = useSessionDispatch('forceLogin'); - const t = useTranslation(); + const { t } = useTranslation(); return ( diff --git a/apps/meteor/client/views/omnichannel/installation/Installation.tsx b/apps/meteor/client/views/omnichannel/installation/Installation.tsx index a6433eed415c..7fedaaa65641 100644 --- a/apps/meteor/client/views/omnichannel/installation/Installation.tsx +++ b/apps/meteor/client/views/omnichannel/installation/Installation.tsx @@ -1,7 +1,8 @@ import { Box } from '@rocket.chat/fuselage'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Page, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; import RawText from '../../../components/RawText'; @@ -10,7 +11,7 @@ import Wrapper from './Wrapper'; // TODO: use `CodeSnippet` Component const Installation = (): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const setting = useSetting('Site_Url') as string; const siteUrl = setting?.replace(/\/$/, ''); diff --git a/apps/meteor/client/views/omnichannel/managers/AddManager.tsx b/apps/meteor/client/views/omnichannel/managers/AddManager.tsx index 24817a03846e..894f3a75f990 100644 --- a/apps/meteor/client/views/omnichannel/managers/AddManager.tsx +++ b/apps/meteor/client/views/omnichannel/managers/AddManager.tsx @@ -1,14 +1,15 @@ import { Button, Box, Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage'; import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks'; import { UserAutoComplete } from '@rocket.chat/ui-client'; -import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; const AddManager = ({ reload }: { reload: () => void }): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const [username, setUsername] = useState(''); const dispatchToastMessage = useToastMessageDispatch(); diff --git a/apps/meteor/client/views/omnichannel/managers/ManagersRoute.tsx b/apps/meteor/client/views/omnichannel/managers/ManagersRoute.tsx index 64aea5dc848c..6cf8d6554d52 100644 --- a/apps/meteor/client/views/omnichannel/managers/ManagersRoute.tsx +++ b/apps/meteor/client/views/omnichannel/managers/ManagersRoute.tsx @@ -1,13 +1,14 @@ -import { usePermission, useTranslation } from '@rocket.chat/ui-contexts'; +import { usePermission } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Page, PageHeader, PageContent } from '../../../components/Page'; import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import ManagersTable from './ManagersTable'; const ManagersRoute = (): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const canViewManagers = usePermission('manage-livechat-managers'); if (!canViewManagers) { diff --git a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx index 753b562cb519..a947447d5c27 100644 --- a/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx +++ b/apps/meteor/client/views/omnichannel/managers/ManagersTable.tsx @@ -26,7 +26,6 @@ const ManagersTable = () => { const t = useTranslation(); const [text, setText] = useState(''); - const debouncedText = useDebouncedValue(text, 500); const { sortBy, sortDirection, setSort } = useSort<'name' | 'username' | 'emails.address'>('name'); @@ -35,12 +34,12 @@ const ManagersTable = () => { const query = useDebouncedValue( useMemo( () => ({ - text: debouncedText, + text, sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`, count: itemsPerPage, offset: current, }), - [debouncedText, sortBy, sortDirection, itemsPerPage, current], + [text, sortBy, sortDirection, itemsPerPage, current], ), 500, ); @@ -79,7 +78,9 @@ const ManagersTable = () => { return ( <> - {((isSuccess && data?.users.length > 0) || queryHasChanged) && } + {((isSuccess && data?.users.length > 0) || queryHasChanged) && ( + setText(event.target.value)} /> + )} {isLoading && ( {headers} @@ -99,7 +100,7 @@ const ManagersTable = () => { )} {isSuccess && data.users.length > 0 && ( <> - + {headers} {data.users.map((user) => ( diff --git a/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx b/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx index 8ce973b7b69f..36c08475982b 100644 --- a/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx +++ b/apps/meteor/client/views/omnichannel/managers/RemoveManagerButton.tsx @@ -1,15 +1,16 @@ import { IconButton } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import GenericModal from '../../../components/GenericModal'; import { GenericTableCell } from '../../../components/GenericTable'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; const RemoveManagerButton = ({ _id, reload }: { _id: string; reload: () => void }): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const deleteAction = useEndpointAction('DELETE', '/v1/livechat/users/manager/:_id', { keys: { _id } }); const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx index 512978abc664..dc87d001f08d 100644 --- a/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/EditTrigger.tsx @@ -1,10 +1,11 @@ import { type ILivechatTrigger, type ILivechatTriggerAction, type Serialized } from '@rocket.chat/core-typings'; import { FieldGroup, Button, ButtonGroup, Field, FieldLabel, FieldRow, FieldError, TextInput, ToggleSwitch } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRouter, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useRouter, useEndpoint } from '@rocket.chat/ui-contexts'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import React, { useMemo } from 'react'; import { Controller, useFieldArray, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { ContextualbarScrollableContent, @@ -74,7 +75,7 @@ const getInitialValues = (triggerData: Serialized | undefined) }); const EditTrigger = ({ triggerData }: { triggerData?: Serialized }) => { - const t = useTranslation(); + const { t } = useTranslation(); const router = useRouter(); const queryClient = useQueryClient(); const dispatchToastMessage = useToastMessageDispatch(); diff --git a/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx b/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx index 10a54494c4fd..bf8196fc7339 100644 --- a/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/EditTriggerWithData.tsx @@ -1,14 +1,15 @@ import type { ILivechatTrigger } from '@rocket.chat/core-typings'; import { Callout } from '@rocket.chat/fuselage'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { ContextualbarSkeleton } from '../../../components/Contextualbar'; import EditTrigger from './EditTrigger'; const EditTriggerWithData = ({ triggerId }: { triggerId: ILivechatTrigger['_id'] }) => { - const t = useTranslation(); + const { t } = useTranslation(); const getTriggersById = useEndpoint('GET', '/v1/livechat/triggers/:_id', { _id: triggerId }); const { data, isLoading, isError } = useQuery(['livechat-getTriggersById', triggerId], async () => getTriggersById(), { refetchOnWindowFocus: false, diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx index c01cd7e80e18..d4df4386ae86 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersPage.tsx @@ -1,6 +1,7 @@ import { Button } from '@rocket.chat/fuselage'; -import { useRouteParameter, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { ContextualbarDialog } from '../../../components/Contextualbar'; import { Page, PageHeader, PageContent } from '../../../components/Page'; @@ -9,7 +10,7 @@ import EditTriggerWithData from './EditTriggerWithData'; import TriggersTable from './TriggersTable'; const TriggersPage = () => { - const t = useTranslation(); + const { t } = useTranslation(); const id = useRouteParameter('id'); const context = useRouteParameter('context'); const router = useRouter(); diff --git a/apps/meteor/client/views/omnichannel/triggers/actions/ActionExternalServiceUrl.tsx b/apps/meteor/client/views/omnichannel/triggers/actions/ActionExternalServiceUrl.tsx index 6b8f11b9203e..c64624d5337e 100644 --- a/apps/meteor/client/views/omnichannel/triggers/actions/ActionExternalServiceUrl.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/actions/ActionExternalServiceUrl.tsx @@ -1,12 +1,13 @@ import { Box, Button, Field, FieldError, FieldHint, FieldLabel, FieldRow, Icon, TextInput } from '@rocket.chat/fuselage'; import { useSafely, useUniqueId } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import type { ComponentProps } from 'react'; import React, { useState } from 'react'; import type { Control, UseFormTrigger } from 'react-hook-form'; import { Controller, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import type { TriggersPayload } from '../EditTrigger'; import { useFieldError } from '../hooks'; @@ -19,7 +20,7 @@ type ActionExternaServicelUrlType = ComponentProps & { }; export const ActionExternalServiceUrl = ({ control, trigger, index, disabled, ...props }: ActionExternaServicelUrlType) => { - const t = useTranslation(); + const { t } = useTranslation(); const serviceUrlFieldId = useUniqueId(); const serviceUrlFieldName = `actions.${index}.params.serviceUrl` as const; diff --git a/apps/meteor/client/views/omnichannel/webhooks/WebhooksPageContainer.tsx b/apps/meteor/client/views/omnichannel/webhooks/WebhooksPageContainer.tsx index 0b59999b24b8..8d9d0ffd05bd 100644 --- a/apps/meteor/client/views/omnichannel/webhooks/WebhooksPageContainer.tsx +++ b/apps/meteor/client/views/omnichannel/webhooks/WebhooksPageContainer.tsx @@ -1,8 +1,9 @@ import type { ISetting, Serialized, SettingValue } from '@rocket.chat/core-typings'; import { Callout } from '@rocket.chat/fuselage'; -import { useEndpoint, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, usePermission } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Page, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page'; import PageSkeleton from '../../../components/PageSkeleton'; @@ -16,7 +17,7 @@ const reduceSettings = (settings: Serialized[]) => }, {}); const WebhooksPageContainer = () => { - const t = useTranslation(); + const { t } = useTranslation(); const getIntegrationsSettings = useEndpoint('GET', '/v1/livechat/integrations.settings'); diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx index c69ce7c43530..8ebd6f114a5f 100644 --- a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx @@ -1,15 +1,18 @@ import type { ICalendarEvent, Serialized } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Button, Palette } from '@rocket.chat/fuselage'; -import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetModal } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime'; import OutlookCalendarEventModal from '../OutlookCalendarEventModal'; import { useOutlookOpenCall } from '../hooks/useOutlookOpenCall'; -const OutlookEventItem = ({ subject, description, startTime, meetingUrl }: Serialized) => { - const t = useTranslation(); +type OutlookEventItemProps = Serialized; + +const OutlookEventItem = ({ subject, description, startTime, meetingUrl }: OutlookEventItemProps) => { + const { t } = useTranslation(); const setModal = useSetModal(); const formatDateAndTime = useFormatDateAndTime(); const openCall = useOutlookOpenCall(meetingUrl); diff --git a/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts index d671c051f8dd..29d6515759fc 100644 --- a/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts +++ b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts @@ -1,5 +1,6 @@ -import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { useTranslation } from 'react-i18next'; import { NotOnDesktopError } from '../lib/NotOnDesktopError'; @@ -38,7 +39,7 @@ export const useOutlookAuthenticationMutation = () => { }; export const useOutlookAuthenticationMutationLogout = () => { - const t = useTranslation(); + const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const mutation = useOutlookAuthenticationMutation(); return useMutation({ diff --git a/apps/meteor/client/views/room/Announcement/Announcement.tsx b/apps/meteor/client/views/room/Announcement/Announcement.tsx index d1ee3b5268c9..58da7d061564 100644 --- a/apps/meteor/client/views/room/Announcement/Announcement.tsx +++ b/apps/meteor/client/views/room/Announcement/Announcement.tsx @@ -1,8 +1,9 @@ import { Box } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetModal } from '@rocket.chat/ui-contexts'; import type { MouseEvent } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import GenericModal from '../../../components/GenericModal'; import MarkdownText from '../../../components/MarkdownText'; @@ -14,7 +15,7 @@ type AnnouncementProps = { }; const Announcement = ({ announcement, announcementDetails }: AnnouncementProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const setModal = useSetModal(); const closeModal = useMutableCallback(() => setModal(null)); const handleClick = (e: MouseEvent): void => { diff --git a/apps/meteor/client/views/room/E2EESetup/RoomE2EENotAllowed.tsx b/apps/meteor/client/views/room/E2EESetup/RoomE2EENotAllowed.tsx index 03beebb8487e..9a41076b246a 100644 --- a/apps/meteor/client/views/room/E2EESetup/RoomE2EENotAllowed.tsx +++ b/apps/meteor/client/views/room/E2EESetup/RoomE2EENotAllowed.tsx @@ -10,9 +10,10 @@ import { StatesTitle, } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; -import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; const DOCS_URL = 'https://go.rocket.chat/i/e2ee-guide'; @@ -26,7 +27,7 @@ type RoomE2EENotAllowedProps = { const RoomE2EENotAllowed = ({ title, subTitle, action, btnText, icon }: RoomE2EENotAllowedProps): ReactElement => { const router = useRouter(); - const t = useTranslation(); + const { t } = useTranslation(); const handleGoHomeClick = () => { router.navigate('/home'); }; diff --git a/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx b/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx index 6f63f8a26ada..fb3cbd97a73c 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx @@ -1,13 +1,14 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderToolbarAction } from '../../../../components/Header'; export const BackButton = ({ routeName }: { routeName?: string }): ReactElement => { const router = useRouter(); - const t = useTranslation(); + const { t } = useTranslation(); const back = useMutableCallback(() => { switch (routeName) { diff --git a/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx b/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx index ff9880ba1e84..bd7d6000db58 100644 --- a/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx +++ b/apps/meteor/client/views/room/Header/RoomToolbox/RoomToolbox.tsx @@ -2,9 +2,10 @@ import type { Box } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { GenericMenu } from '@rocket.chat/ui-client'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; -import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; +import { useLayout } from '@rocket.chat/ui-contexts'; import type { ComponentProps } from 'react'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderToolbarAction, HeaderToolbarDivider } from '../../../../components/Header'; import { useRoomToolbox } from '../../contexts/RoomToolboxContext'; @@ -20,7 +21,7 @@ type MenuActionsProps = { }[]; const RoomToolbox = ({ className }: RoomToolboxProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const { roomToolboxExpanded } = useLayout(); const toolbox = useRoomToolbox(); diff --git a/apps/meteor/client/views/room/Header/icons/Encrypted.tsx b/apps/meteor/client/views/room/Header/icons/Encrypted.tsx index 71acc234f885..ca21153126fd 100644 --- a/apps/meteor/client/views/room/Header/icons/Encrypted.tsx +++ b/apps/meteor/client/views/room/Header/icons/Encrypted.tsx @@ -1,12 +1,13 @@ import type { IRoom } from '@rocket.chat/core-typings'; import colors from '@rocket.chat/fuselage-tokens/colors'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderState } from '../../../../components/Header'; const Encrypted = ({ room }: { room: IRoom }) => { - const t = useTranslation(); + const { t } = useTranslation(); const e2eEnabled = useSetting('E2E_Enable'); return e2eEnabled && room?.encrypted ? : null; }; diff --git a/apps/meteor/client/views/room/Header/icons/Translate.tsx b/apps/meteor/client/views/room/Header/icons/Translate.tsx index 701de69cb679..993b844a4213 100644 --- a/apps/meteor/client/views/room/Header/icons/Translate.tsx +++ b/apps/meteor/client/views/room/Header/icons/Translate.tsx @@ -1,6 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderState } from '../../../../components/Header'; @@ -9,7 +10,7 @@ type TranslateProps = { }; const Translate = ({ room: { autoTranslateLanguage, autoTranslate } }: TranslateProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const autoTranslateEnabled = useSetting('AutoTranslate_Enabled'); const encryptedLabel = t('Translated'); return autoTranslateEnabled && autoTranslate && autoTranslateLanguage ? ( diff --git a/apps/meteor/client/views/room/HeaderV2/Omnichannel/BackButton.tsx b/apps/meteor/client/views/room/HeaderV2/Omnichannel/BackButton.tsx index fc9ef65593d8..5ad221129ae3 100644 --- a/apps/meteor/client/views/room/HeaderV2/Omnichannel/BackButton.tsx +++ b/apps/meteor/client/views/room/HeaderV2/Omnichannel/BackButton.tsx @@ -1,7 +1,8 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderToolbarAction } from '../../../../components/Header'; @@ -9,7 +10,7 @@ type BackButtonProps = { routeName?: string }; const BackButton = ({ routeName }: BackButtonProps): ReactElement => { const router = useRouter(); - const t = useTranslation(); + const { t } = useTranslation(); const back = useMutableCallback(() => { switch (routeName) { diff --git a/apps/meteor/client/views/room/HeaderV2/RoomToolbox/RoomToolbox.tsx b/apps/meteor/client/views/room/HeaderV2/RoomToolbox/RoomToolbox.tsx index abf9b41b89da..5c0aeb430478 100644 --- a/apps/meteor/client/views/room/HeaderV2/RoomToolbox/RoomToolbox.tsx +++ b/apps/meteor/client/views/room/HeaderV2/RoomToolbox/RoomToolbox.tsx @@ -2,9 +2,10 @@ import type { Box } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { GenericMenu } from '@rocket.chat/ui-client'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; -import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; +import { useLayout } from '@rocket.chat/ui-contexts'; import type { ComponentProps } from 'react'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderToolbarAction, HeaderToolbarDivider } from '../../../../components/Header'; import { useRoomToolbox } from '../../contexts/RoomToolboxContext'; @@ -20,7 +21,7 @@ type MenuActionsProps = { }[]; const RoomToolbox = ({ className }: RoomToolboxProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const { roomToolboxExpanded } = useLayout(); const toolbox = useRoomToolbox(); diff --git a/apps/meteor/client/views/room/HeaderV2/icons/Encrypted.tsx b/apps/meteor/client/views/room/HeaderV2/icons/Encrypted.tsx index 71acc234f885..ca21153126fd 100644 --- a/apps/meteor/client/views/room/HeaderV2/icons/Encrypted.tsx +++ b/apps/meteor/client/views/room/HeaderV2/icons/Encrypted.tsx @@ -1,12 +1,13 @@ import type { IRoom } from '@rocket.chat/core-typings'; import colors from '@rocket.chat/fuselage-tokens/colors'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderState } from '../../../../components/Header'; const Encrypted = ({ room }: { room: IRoom }) => { - const t = useTranslation(); + const { t } = useTranslation(); const e2eEnabled = useSetting('E2E_Enable'); return e2eEnabled && room?.encrypted ? : null; }; diff --git a/apps/meteor/client/views/room/HeaderV2/icons/Translate.tsx b/apps/meteor/client/views/room/HeaderV2/icons/Translate.tsx index 0097c9f2e3d8..993b844a4213 100644 --- a/apps/meteor/client/views/room/HeaderV2/icons/Translate.tsx +++ b/apps/meteor/client/views/room/HeaderV2/icons/Translate.tsx @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC } from 'react'; +import { useSetting } from '@rocket.chat/ui-contexts'; import React, { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import { HeaderState } from '../../../../components/Header'; @@ -9,8 +9,8 @@ type TranslateProps = { room: IRoom; }; -const Translate: FC = ({ room: { autoTranslateLanguage, autoTranslate } }) => { - const t = useTranslation(); +const Translate = ({ room: { autoTranslateLanguage, autoTranslate } }: TranslateProps) => { + const { t } = useTranslation(); const autoTranslateEnabled = useSetting('AutoTranslate_Enabled'); const encryptedLabel = t('Translated'); return autoTranslateEnabled && autoTranslate && autoTranslateLanguage ? ( diff --git a/apps/meteor/client/views/room/RoomAnnouncement/RoomAnnouncement.tsx b/apps/meteor/client/views/room/RoomAnnouncement/RoomAnnouncement.tsx index 4c96b88bb348..7c8db93f3762 100644 --- a/apps/meteor/client/views/room/RoomAnnouncement/RoomAnnouncement.tsx +++ b/apps/meteor/client/views/room/RoomAnnouncement/RoomAnnouncement.tsx @@ -1,8 +1,9 @@ import { Box } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC, MouseEvent } from 'react'; +import { useSetModal } from '@rocket.chat/ui-contexts'; +import type { MouseEvent } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import GenericModal from '../../../components/GenericModal'; import MarkdownText from '../../../components/MarkdownText'; @@ -13,8 +14,8 @@ type RoomAnnouncementParams = { announcementDetails?: () => void; }; -const RoomAnnouncement: FC = ({ announcement, announcementDetails }) => { - const t = useTranslation(); +const RoomAnnouncement = ({ announcement, announcementDetails }: RoomAnnouncementParams) => { + const { t } = useTranslation(); const setModal = useSetModal(); const closeModal = useMutableCallback(() => setModal(null)); const handleClick = (e: MouseEvent): void => { diff --git a/apps/meteor/client/views/room/RoomNotFound.tsx b/apps/meteor/client/views/room/RoomNotFound.tsx index 12aa61deef50..fa92771a2267 100644 --- a/apps/meteor/client/views/room/RoomNotFound.tsx +++ b/apps/meteor/client/views/room/RoomNotFound.tsx @@ -1,15 +1,16 @@ import { Box } from '@rocket.chat/fuselage'; import { Header, HeaderToolbar } from '@rocket.chat/ui-client'; -import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; +import { useLayout } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import NotFoundState from '../../components/NotFoundState'; import SidebarToggler from '../../components/SidebarToggler'; import RoomLayout from './layout/RoomLayout'; const RoomNotFound = (): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const { isMobile } = useLayout(); return ( diff --git a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx index feaf12fd6b04..0ab24255a204 100644 --- a/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx +++ b/apps/meteor/client/views/room/UserCard/UserCardWithData.tsx @@ -1,9 +1,10 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { GenericMenu } from '@rocket.chat/ui-client'; -import { useSetting, useRolesDescription, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting, useRolesDescription } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { getUserDisplayName } from '../../../../lib/getUserDisplayName'; import LocalTime from '../../../components/LocalTime'; @@ -12,6 +13,7 @@ import { ReactiveUserStatus } from '../../../components/UserStatus'; import { useUserInfoQuery } from '../../../hooks/useUserInfoQuery'; import { useMemberExists } from '../../hooks/useMemberExists'; import { useUserInfoActions } from '../hooks/useUserInfoActions'; +import type { UserInfoAction } from '../hooks/useUserInfoActions/useUserInfoActions'; type UserCardWithDataProps = { username: string; @@ -21,7 +23,7 @@ type UserCardWithDataProps = { }; const UserCardWithData = ({ username, rid, onOpenUserInfo, onClose }: UserCardWithDataProps) => { - const t = useTranslation(); + const { t } = useTranslation(); const getRoles = useRolesDescription(); const showRealNames = Boolean(useSetting('UI_Use_Real_Name')); @@ -97,8 +99,8 @@ const UserCardWithData = ({ username, rid, onOpenUserInfo, onClose }: UserCardWi }, [menuOptions, onClose, t]); const actions = useMemo(() => { - const mapAction = ([key, { content, title, icon, onClick }]: any): ReactElement => ( - + const mapAction = ([key, { content, title, icon, onClick }]: [string, UserInfoAction]): ReactElement => ( + ); return [...actionsDefinition.map(mapAction), menu].filter(Boolean); diff --git a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts index 9cb94e8c93f0..5aa8eae0df07 100644 --- a/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts +++ b/apps/meteor/client/views/room/body/hooks/useGoToHomeOnRemoved.ts @@ -1,7 +1,8 @@ import { isOmnichannelRoom, type IRoom } from '@rocket.chat/core-typings'; -import { useRouter, useStream, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useStream, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { useOmnichannelCloseRoute } from '../../../../hooks/omnichannel/useOmnichannelCloseRoute'; @@ -10,7 +11,7 @@ export function useGoToHomeOnRemoved(room: IRoom, userId?: string): void { const queryClient = useQueryClient(); const dispatchToastMessage = useToastMessageDispatch(); const subscribeToNotifyUser = useStream('notify-user'); - const t = useTranslation(); + const { t } = useTranslation(); const { navigateHome } = useOmnichannelCloseRoute(); useEffect(() => { diff --git a/apps/meteor/client/views/room/composer/ComposerOmnichannel/ComposerOmnichannelJoin.tsx b/apps/meteor/client/views/room/composer/ComposerOmnichannel/ComposerOmnichannelJoin.tsx index 62dd7ef88fb8..8daf8dc3156a 100644 --- a/apps/meteor/client/views/room/composer/ComposerOmnichannel/ComposerOmnichannelJoin.tsx +++ b/apps/meteor/client/views/room/composer/ComposerOmnichannel/ComposerOmnichannelJoin.tsx @@ -1,7 +1,8 @@ import { MessageFooterCallout, MessageFooterCalloutAction, MessageFooterCalloutContent } from '@rocket.chat/ui-composer'; -import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { useOmnichannelRoom } from '../../contexts/RoomContext'; @@ -11,7 +12,7 @@ export const ComposerOmnichannelJoin = (): ReactElement => { const dispatchToastMessage = useToastMessageDispatch(); - const t = useTranslation(); + const { t } = useTranslation(); return ( {t('room_is_read_only')} diff --git a/apps/meteor/client/views/room/composer/ComposerReadOnly.tsx b/apps/meteor/client/views/room/composer/ComposerReadOnly.tsx index 01156121c3e5..f71879d520c2 100644 --- a/apps/meteor/client/views/room/composer/ComposerReadOnly.tsx +++ b/apps/meteor/client/views/room/composer/ComposerReadOnly.tsx @@ -1,15 +1,16 @@ import { Button } from '@rocket.chat/fuselage'; import { MessageFooterCallout, MessageFooterCalloutContent } from '@rocket.chat/ui-composer'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { dispatchToastMessage } from '../../../lib/toast'; import { useRoom, useUserIsSubscribed } from '../contexts/RoomContext'; const ComposerReadOnly = (): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const room = useRoom(); const isSubscribed = useUserIsSubscribed(); const joinChannel = useEndpoint('POST', '/v1/channels.join'); diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useShareLocationAction.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useShareLocationAction.tsx index a49253c2744d..24c801bae208 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useShareLocationAction.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useShareLocationAction.tsx @@ -1,8 +1,9 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; -import { useSetting, useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting, useSetModal } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import ShareLocationModal from '../../../../ShareLocation/ShareLocationModal'; @@ -11,7 +12,7 @@ export const useShareLocationAction = (room?: IRoom, tmid?: IMessage['tmid']): G throw new Error('Invalid room'); } - const t = useTranslation(); + const { t } = useTranslation(); const setModal = useSetModal(); const isMapViewEnabled = useSetting('MapView_Enabled') === true; diff --git a/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx b/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx index 6cc6e692640d..9b2c6217e52a 100644 --- a/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx +++ b/apps/meteor/client/views/room/contextualBar/Discussions/DiscussionsList.tsx @@ -1,9 +1,10 @@ import type { IDiscussionMessage } from '@rocket.chat/core-typings'; import { Box, Icon, TextInput, Callout, Throbber } from '@rocket.chat/fuselage'; import { useResizeObserver, useAutoFocus } from '@rocket.chat/fuselage-hooks'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetting } from '@rocket.chat/ui-contexts'; import type { RefObject } from 'react'; import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; import { Virtuoso } from 'react-virtuoso'; import { @@ -40,7 +41,7 @@ function DiscussionsList({ text, onChangeFilter, }: DiscussionsListProps) { - const t = useTranslation(); + const { t } = useTranslation(); const showRealNames = useSetting('UI_Use_Real_Name') || false; const inputRef = useAutoFocus(true); diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx index 41b6c33fec6c..2cb94cd6576f 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx @@ -163,7 +163,7 @@ const MailExportForm = ({ formId, rid, onCancel, exportOptions }: MailExportForm return undefined; } - return t('Mail_Message_Invalid_emails', additionalEmails); + return t('Mail_Message_Invalid_emails', { postProcess: 'sprintf', sprintf: [additionalEmails] }); }, validateToUsers: (additionalEmails) => { if (additionalEmails !== '' || toUsers?.length > 0) { diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/useRoomExportMutation.ts b/apps/meteor/client/views/room/contextualBar/ExportMessages/useRoomExportMutation.ts index ef8d67e3af3d..5f85a8536ab4 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/useRoomExportMutation.ts +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/useRoomExportMutation.ts @@ -1,8 +1,9 @@ -import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; +import { useTranslation } from 'react-i18next'; export const useRoomExportMutation = () => { - const t = useTranslation(); + const { t } = useTranslation(); const roomsExport = useEndpoint('POST', '/v1/rooms.export'); const dispatchToastMessage = useToastMessageDispatch(); diff --git a/apps/meteor/client/views/room/contextualBar/MentionsTab.tsx b/apps/meteor/client/views/room/contextualBar/MentionsTab.tsx index 23be0abc8bb0..42a643e9971a 100644 --- a/apps/meteor/client/views/room/contextualBar/MentionsTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/MentionsTab.tsx @@ -1,8 +1,9 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { mapMessageFromApi } from '../../../lib/utils/mapMessageFromApi'; import { useRoom } from '../contexts/RoomContext'; @@ -28,7 +29,7 @@ const MentionsTab = (): ReactElement => { return messages; }); - const t = useTranslation(); + const { t } = useTranslation(); return ( @@ -62,7 +62,7 @@ const OTR = ({ isOnline, onClickClose, onClickStart, onClickEnd, onClickRefresh, return ( diff --git a/apps/meteor/client/views/room/contextualBar/PinnedMessagesTab.tsx b/apps/meteor/client/views/room/contextualBar/PinnedMessagesTab.tsx index dcdab0612db9..102fc46ebfa8 100644 --- a/apps/meteor/client/views/room/contextualBar/PinnedMessagesTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/PinnedMessagesTab.tsx @@ -1,8 +1,9 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { onClientMessageReceived } from '../../../lib/onClientMessageReceived'; import { mapMessageFromApi } from '../../../lib/utils/mapMessageFromApi'; @@ -29,7 +30,7 @@ const PinnedMessagesTab = (): ReactElement => { return Promise.all(messages.map(onClientMessageReceived)); }); - const t = useTranslation(); + const { t } = useTranslation(); return ( { - const t = useTranslation(); + const { t } = useTranslation(); const room = useRoom(); const setModal = useSetModal(); const { closeTab: close } = useRoomToolbox(); @@ -119,14 +120,15 @@ const PruneMessagesWithData = (): ReactElement => { }); const callOutText = useMemo(() => { - const exceptPinned = pinned ? ` ${t('except_pinned', {})}` : ''; + const name = room && ((isDirectMessageRoom(room) && room.usernames?.join(' x ')) || room.fname || room.name); + const exceptPinned = pinned ? ` ${t('except_pinned')}` : ''; const ifFrom = users.length ? ` ${t('if_they_are_from', { postProcess: 'sprintf', sprintf: [users.map((element) => element).join(', ')], })}` : ''; - const filesOrMessages = t(attached ? 'files' : 'messages', {}); + const filesOrMessages = attached ? t('files') : t('messages'); if (newerDate && olderDate) { return ( @@ -164,7 +166,7 @@ const PruneMessagesWithData = (): ReactElement => { return ( t('Prune_Warning_all', { postProcess: 'sprintf', - sprintf: [filesOrMessages, room && ((isDirectMessageRoom(room) && room.usernames?.join(' x ')) || room.fname || room.name)], + sprintf: [filesOrMessages, name], }) + exceptPinned + ifFrom @@ -173,17 +175,11 @@ const PruneMessagesWithData = (): ReactElement => { const validateText = useMemo(() => { if (fromDate > toDate) { - return t('Newer_than_may_not_exceed_Older_than', { - postProcess: 'sprintf', - sprintf: [], - }); + return t('Newer_than_may_not_exceed_Older_than'); } if (isNaN(fromDate.getTime()) || isNaN(toDate.getTime())) { - return t('error-invalid-date', { - postProcess: 'sprintf', - sprintf: [], - }); + return t('error-invalid-date'); } return undefined; diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useDeleteFile.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useDeleteFile.tsx index bbc6676ff93c..59e6bc769492 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useDeleteFile.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/hooks/useDeleteFile.tsx @@ -1,11 +1,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetModal, useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../../components/GenericModal'; export const useDeleteFile = (reload: () => void) => { - const t = useTranslation(); + const { t } = useTranslation(); const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); const deleteFile = useMethod('deleteFileMessage'); diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx index 4ae17296a53e..730ed18e2d65 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddMatrixUsers/AddMatrixUsersModal.tsx @@ -1,8 +1,9 @@ import { Modal, Button, Box, Icon } from '@rocket.chat/fuselage'; -import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import React from 'react'; import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; type AddMatrixUsersModalProps = { matrixIdVerifiedStatus: Map; @@ -48,7 +49,7 @@ const AddMatrixUsersModal = ({ onClose, matrixIdVerifiedStatus, onSave, complete .catch((error) => dispatchToastMessage({ type: 'error', message: error as Error })); }; - const t = useTranslation(); + const { t } = useTranslation(); return ( diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx index 12a84dacd19e..8612583a7648 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/AddUsers/AddUsers.tsx @@ -2,10 +2,11 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; import { Field, FieldLabel, Button, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; import { Controller, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { ContextualbarHeader, @@ -28,7 +29,7 @@ type AddUsersProps = { }; const AddUsers = ({ rid, onClickBack, reload }: AddUsersProps): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const room = useRoom(); diff --git a/apps/meteor/client/views/room/contextualBar/StarredMessagesTab.tsx b/apps/meteor/client/views/room/contextualBar/StarredMessagesTab.tsx index 4159f570f5c9..f835ae416983 100644 --- a/apps/meteor/client/views/room/contextualBar/StarredMessagesTab.tsx +++ b/apps/meteor/client/views/room/contextualBar/StarredMessagesTab.tsx @@ -1,7 +1,8 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { onClientMessageReceived } from '../../../lib/onClientMessageReceived'; import { mapMessageFromApi } from '../../../lib/utils/mapMessageFromApi'; @@ -28,7 +29,7 @@ const StarredMessagesTab = () => { return Promise.all(messages.map(onClientMessageReceived)); }); - const t = useTranslation(); + const { t } = useTranslation(); return ( { - const t = useTranslation(); + const { t } = useTranslation(); const { innerRef, bubbleRef, listStyle, ...bubbleDate } = useDateScroll(); const { messages, loading } = useLegacyThreadMessages(mainMessage._id); diff --git a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx index 2e3255ea2e0a..d22f3b585122 100644 --- a/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/UserInfo/UserInfoWithData.tsx @@ -1,8 +1,9 @@ import type { IUser, IRoom } from '@rocket.chat/core-typings'; import { Callout } from '@rocket.chat/fuselage'; -import { useRolesDescription, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRolesDescription } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { getUserEmailAddress } from '../../../../../lib/getUserEmailAddress'; import { @@ -31,7 +32,7 @@ type UserInfoWithDataProps = { }; const UserInfoWithData = ({ uid, username, rid, onClose, onClickBack }: UserInfoWithDataProps): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const getRoles = useRolesDescription(); const { diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts index 3a46a5310c1b..4a1a73034726 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts @@ -1,10 +1,11 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { usePermission, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { usePermission, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation } from 'react-i18next'; import type { UserInfoAction, UserInfoActionType } from '../useUserInfoActions'; export const useRedirectModerationConsole = (uid: IUser['_id']): UserInfoAction | undefined => { - const t = useTranslation(); + const { t } = useTranslation(); const hasPermissionToView = usePermission('view-moderation-console'); const router = useRoute('moderation-console'); diff --git a/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptsModal.tsx b/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptsModal.tsx index ca033c2dcb0d..4f99ac5be8c4 100644 --- a/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptsModal.tsx +++ b/apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptsModal.tsx @@ -1,8 +1,9 @@ import type { IMessage, ReadReceipt } from '@rocket.chat/core-typings'; -import { useMethod, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMethod, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import GenericModal from '../../../../components/GenericModal'; import GenericModalSkeleton from '../../../../components/GenericModal/GenericModalSkeleton'; @@ -14,7 +15,7 @@ type ReadReceiptsModalProps = { }; const ReadReceiptsModal = ({ messageId, onClose }: ReadReceiptsModalProps): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const getReadReceipts = useMethod('getReadReceipts'); diff --git a/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx index 245a2a0112f3..8beb6612eaa0 100644 --- a/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx +++ b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx @@ -2,9 +2,10 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { useMethod, useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useMethod, useSetting, useUserPreference } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; import type { ReactNode } from 'react'; +import { useTranslation } from 'react-i18next'; import { hasAtLeastOnePermission } from '../../../../app/authorization/client'; import { CannedResponse } from '../../../../app/canned-responses/client/collections/CannedResponse'; @@ -24,7 +25,12 @@ import type { ComposerBoxPopupUserProps } from '../composer/ComposerBoxPopupUser import type { ComposerPopupContextValue } from '../contexts/ComposerPopupContext'; import { ComposerPopupContext, createMessageBoxPopupConfig } from '../contexts/ComposerPopupContext'; -const ComposerPopupProvider = ({ children, room }: { children: ReactNode; room: IRoom }) => { +type ComposerPopupProviderProps = { + children: ReactNode; + room: IRoom; +}; + +const ComposerPopupProvider = ({ children, room }: ComposerPopupProviderProps) => { const { _id: rid, encrypted: isRoomEncrypted } = room; const userSpotlight = useMethod('spotlight'); const suggestionsCount = useSetting('Number_of_users_autocomplete_suggestions'); @@ -32,7 +38,7 @@ const ComposerPopupProvider = ({ children, room }: { children: ReactNode; room: const [recentEmojis] = useLocalStorage('emoji.recent', []); const isOmnichannel = isOmnichannelRoom(room); const useEmoji = useUserPreference('useEmojis'); - const t = useTranslation(); + const { t, i18n } = useTranslation(); const e2eEnabled = useSetting('E2E_Enable'); const unencryptedMessagesAllowed = useSetting('E2E_Allow_Unencrypted_Messages'); const encrypted = isRoomEncrypted && e2eEnabled && !unencryptedMessagesAllowed; @@ -289,8 +295,8 @@ const ComposerPopupProvider = ({ children, room }: { children: ReactNode; room: const item = slashCommands.commands[command]; return { _id: command, - params: item.params && t.has(item.params) ? t(item.params) : item.params ?? '', - description: item.description && t.has(item.description) ? t(item.description) : item.description, + params: item.params && i18n.exists(item.params) ? t(item.params) : item.params ?? '', + description: item.description && i18n.exists(item.description) ? t(item.description) : item.description, permission: item.permission, ...(encrypted && { disabled: encrypted }), }; @@ -365,7 +371,7 @@ const ComposerPopupProvider = ({ children, room }: { children: ReactNode; room: }, }), ].filter(Boolean); - }, [t, cannedResponseEnabled, isOmnichannel, recentEmojis, suggestionsCount, userSpotlight, rid, call, useEmoji, encrypted]); + }, [t, i18n, cannedResponseEnabled, isOmnichannel, recentEmojis, suggestionsCount, userSpotlight, rid, call, useEmoji, encrypted]); return ; }; diff --git a/apps/meteor/client/views/room/webdav/AddWebdavAccountModal.tsx b/apps/meteor/client/views/room/webdav/AddWebdavAccountModal.tsx index 63ebaecc5eb6..94de5d4380c7 100644 --- a/apps/meteor/client/views/room/webdav/AddWebdavAccountModal.tsx +++ b/apps/meteor/client/views/room/webdav/AddWebdavAccountModal.tsx @@ -1,10 +1,11 @@ import type { IWebdavAccountPayload } from '@rocket.chat/core-typings'; import { Modal, Field, FieldGroup, FieldLabel, FieldRow, FieldError, TextInput, PasswordInput, Button, Box } from '@rocket.chat/fuselage'; -import { useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState } from 'react'; import type { SubmitHandler } from 'react-hook-form'; import { useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; type AddWebdavAccountModalPayload = IWebdavAccountPayload; @@ -22,7 +23,7 @@ const AddWebdavAccountModal = ({ onClose, onConfirm }: AddWebdavAccountModalProp handleSubmit, formState: { errors }, } = useForm(); - const t = useTranslation(); + const { t } = useTranslation(); const onSubmit: SubmitHandler = async (data) => { setIsLoading(true); diff --git a/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx b/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx index e64a4e33c1bb..f2a25fa4616d 100644 --- a/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx +++ b/apps/meteor/client/views/room/webdav/SaveToWebdavModal.tsx @@ -2,10 +2,11 @@ import type { MessageAttachment, IWebdavAccount } from '@rocket.chat/core-typing import type { SelectOption } from '@rocket.chat/fuselage'; import { Modal, Box, Button, FieldGroup, Field, FieldLabel, FieldRow, FieldError, Select, Throbber } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; -import { useMethod, useSetting, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMethod, useSetting, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState, useMemo, useEffect, useRef } from 'react'; import { useForm, Controller } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { useWebDAVAccountIntegrationsQuery } from '../../../hooks/webdav/useWebDAVAccountIntegrationsQuery'; import { getWebdavServerName } from '../../../lib/getWebdavServerName'; @@ -19,7 +20,7 @@ type SaveToWebdavModalProps = { }; const SaveToWebdavModal = ({ onClose, data }: SaveToWebdavModalProps): ReactElement => { - const t = useTranslation(); + const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const dispatchToastMessage = useToastMessageDispatch(); const uploadFileToWebdav = useMethod('uploadFileToWebdav'); diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerModal.tsx b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerModal.tsx index 38895d4c211e..b6669b3f5d77 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerModal.tsx +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerModal.tsx @@ -36,7 +36,7 @@ const WebdavFilePickerModal = ({ onUpload, onClose, account }: WebdavFilePickerM const [parentFolders, setParentFolders] = useState([]); const [webdavNodes, setWebdavNodes] = useState([]); const [filterText, setFilterText] = useState(''); - const debouncedFilter = useDebouncedValue(filterText, 500); + const debouncedFilter = useDebouncedValue('', 500); const [isLoading, setIsLoading] = useState(false); const showFilePreviews = useMutableCallback(async (accountId, nodes) => { @@ -196,7 +196,7 @@ const WebdavFilePickerModal = ({ onUpload, onClose, account }: WebdavFilePickerM - + setFilterText(event.target.value)}> {typeView === 'grid' && (