From ba856a2a776545e764e116ecbc6160524bbdd5a8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 11 Sep 2023 10:57:53 -0300 Subject: [PATCH 01/17] chore: remove istanbul from prod env (#30313) --- .github/actions/build-docker/action.yml | 73 +++++++++ .github/actions/meteor-build/action.yml | 129 ++++++++++++++++ .github/workflows/ci.yml | 196 +++++++----------------- apps/meteor/.babelrc | 20 ++- 4 files changed, 268 insertions(+), 150 deletions(-) create mode 100644 .github/actions/build-docker/action.yml create mode 100644 .github/actions/meteor-build/action.yml diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml new file mode 100644 index 000000000000..808b8acdcbe3 --- /dev/null +++ b/.github/actions/build-docker/action.yml @@ -0,0 +1,73 @@ +name: 'Meteor Docker' + +inputs: + CR_USER: + required: true + CR_PAT: + required: true + node-version: + required: true + description: 'Node version' + type: string + platform: + required: false + description: 'Platform' + type: string + +runs: + using: composite + + steps: + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ inputs.CR_USER }} + password: ${{ inputs.CR_PAT }} + + - name: Restore build + uses: actions/download-artifact@v3 + with: + name: build + path: /tmp/build + + - name: Unpack build + shell: bash + run: | + cd /tmp/build + tar xzf Rocket.Chat.tar.gz + rm Rocket.Chat.tar.gz + + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: ${{ inputs.node-version }} + cache-modules: true + install: true + + - run: yarn build + shell: bash + + - name: Build Docker images + shell: bash + run: | + args=(rocketchat) + + if [[ '${{ inputs.platform }}' = 'alpine' ]]; then + args+=($SERVICES_PUBLISH) + fi; + + docker compose -f docker-compose-ci.yml build "${args[@]}" + + - name: Publish Docker images to GitHub Container Registry + shell: bash + run: | + args=(rocketchat) + + if [[ '${{ inputs.platform }}' = 'alpine' ]]; then + args+=($SERVICES_PUBLISH) + fi; + + docker compose -f docker-compose-ci.yml push "${args[@]}" diff --git a/.github/actions/meteor-build/action.yml b/.github/actions/meteor-build/action.yml new file mode 100644 index 000000000000..21fec059c8de --- /dev/null +++ b/.github/actions/meteor-build/action.yml @@ -0,0 +1,129 @@ +name: 'Meteor Build' + +inputs: + coverage: + required: false + description: 'Enable coverage' + type: boolean + reset-meteor: + required: false + description: 'Reset Meteor' + type: boolean + node-version: + required: true + description: 'Node version' + type: string + +runs: + using: composite + + steps: + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 4 + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + node-version: ${{ inputs.node-version }} + cache-modules: true + install: true + + # - name: Free disk space + # run: | + # sudo apt clean + # docker rmi $(docker image ls -aq) + # df -h + + - name: Cache vite + uses: actions/cache@v3 + with: + path: ./node_modules/.vite + key: vite-local-cache-${{ runner.OS }}-${{ hashFiles('package.json') }} + restore-keys: | + vite-local-cache-${{ runner.os }}- + + - name: Cache meteor local + uses: actions/cache@v3 + with: + path: ./apps/meteor/.meteor/local + key: meteor-local-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/versions') }} + restore-keys: | + meteor-local-cache-${{ runner.os }}- + + - name: Cache meteor + uses: actions/cache@v3 + with: + path: ~/.meteor + key: meteor-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/release') }} + restore-keys: | + meteor-cache-${{ runner.os }}- + + - name: Install Meteor + shell: bash + run: | + # Restore bin from cache + set +e + METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor) + METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET") + set -e + LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor + if [ -e $LAUNCHER ] + then + echo "Cached Meteor bin found, restoring it" + sudo cp "$LAUNCHER" "/usr/local/bin/meteor" + else + echo "No cached Meteor bin found." + fi + + # only install meteor if bin isn't found + command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh + + - name: Versions + shell: bash + run: | + npm --versions + yarn -v + node -v + meteor --version + meteor npm --versions + meteor node -v + git version + + - uses: dtinth/setup-github-actions-caching-for-turbo@v1 + + - name: Translation check + shell: bash + run: yarn turbo run translation-check + + - name: Reset Meteor + shell: bash + if: ${{ inputs.reset-meteor == 'true' }} + working-directory: ./apps/meteor + run: meteor reset + + - name: Build Rocket.Chat From Pull Request + shell: bash + if: startsWith(github.ref, 'refs/pull/') == true + env: + METEOR_PROFILE: 1000 + BABEL_ENV: ${{ inputs.coverage == 'true' && 'coverage' || '' }} + run: yarn build:ci -- --directory /tmp/dist + + - name: Build Rocket.Chat + shell: bash + if: startsWith(github.ref, 'refs/pull/') != true + run: yarn build:ci -- --directory /tmp/dist + + - name: Prepare build + shell: bash + run: | + cd /tmp/dist + tar czf /tmp/Rocket.Chat.tar.gz bundle + + - name: Store build + uses: actions/upload-artifact@v3 + with: + name: build + path: /tmp/Rocket.Chat.tar.gz diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70ec4dcba6ed..22250705ea58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,7 +123,7 @@ jobs: run: yarn build build: - name: 📦 Meteor Build + name: 📦 Meteor Build - coverage needs: [release-versions, packages-build] runs-on: ubuntu-20.04 @@ -138,111 +138,38 @@ jobs: echo "github.event_name: ${{ github.event_name }}" cat $GITHUB_EVENT_PATH - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 4 - - uses: actions/checkout@v3 - - name: Setup NodeJS - uses: ./.github/actions/setup-node + - uses: ./.github/actions/meteor-build with: node-version: ${{ needs.release-versions.outputs.node-version }} - cache-modules: true - install: true - - # - name: Free disk space - # run: | - # sudo apt clean - # docker rmi $(docker image ls -aq) - # df -h - - - name: Cache vite - uses: actions/cache@v3 - with: - path: ./node_modules/.vite - key: vite-local-cache-${{ runner.OS }}-${{ hashFiles('package.json') }} - restore-keys: | - vite-local-cache-${{ runner.os }}- - - - name: Cache meteor local - uses: actions/cache@v3 - with: - path: ./apps/meteor/.meteor/local - key: meteor-local-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/versions') }} - restore-keys: | - meteor-local-cache-${{ runner.os }}- - - - name: Cache meteor - uses: actions/cache@v3 - with: - path: ~/.meteor - key: meteor-cache-${{ runner.OS }}-${{ hashFiles('apps/meteor/.meteor/release') }} - restore-keys: | - meteor-cache-${{ runner.os }}- + coverage: true - - name: Install Meteor - run: | - # Restore bin from cache - set +e - METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor) - METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET") - set -e - LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor - if [ -e $LAUNCHER ] - then - echo "Cached Meteor bin found, restoring it" - sudo cp "$LAUNCHER" "/usr/local/bin/meteor" - else - echo "No cached Meteor bin found." - fi - - # only install meteor if bin isn't found - command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh + build-prod: + name: 📦 Meteor Build - official + needs: [tests-done, release-versions, packages-build] + if: (github.event_name == 'release' || github.ref == 'refs/heads/develop') + runs-on: ubuntu-20.04 - - name: Versions + steps: + - name: Github Info run: | - npm --versions - yarn -v - node -v - meteor --version - meteor npm --versions - meteor node -v - git version - - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - - - name: Translation check - run: yarn turbo run translation-check - - - name: Reset Meteor - if: startsWith(github.ref, 'refs/tags/') == 'true' || github.ref == 'refs/heads/develop' - working-directory: ./apps/meteor - run: meteor reset - - - name: Build Rocket.Chat From Pull Request - if: startsWith(github.ref, 'refs/pull/') == true - env: - METEOR_PROFILE: 1000 - run: yarn build:ci -- --directory /tmp/dist - - - name: Build Rocket.Chat - if: startsWith(github.ref, 'refs/pull/') != true - run: yarn build:ci -- --directory /tmp/dist + echo "GITHUB_ACTION: $GITHUB_ACTION" + echo "GITHUB_ACTOR: $GITHUB_ACTOR" + echo "GITHUB_REF: $GITHUB_REF" + echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF" + echo "GITHUB_BASE_REF: $GITHUB_BASE_REF" + echo "github.event_name: ${{ github.event_name }}" + cat $GITHUB_EVENT_PATH - - name: Prepare build - run: | - cd /tmp/dist - tar czf /tmp/Rocket.Chat.tar.gz bundle + - uses: actions/checkout@v3 - - name: Store build - uses: actions/upload-artifact@v3 + - uses: ./.github/actions/meteor-build with: - name: build - path: /tmp/Rocket.Chat.tar.gz + node-version: ${{ needs.release-versions.outputs.node-version }} + coverage: false - build-gh-docker: + build-gh-docker-coverage: name: 🚢 Build Docker Images for Testing needs: [build, release-versions] if: (github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'release' || github.ref == 'refs/heads/develop') @@ -262,56 +189,39 @@ jobs: steps: - uses: actions/checkout@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ secrets.CR_USER }} - password: ${{ secrets.CR_PAT }} - - - name: Restore build - uses: actions/download-artifact@v3 - with: - name: build - path: /tmp/build - - - name: Unpack build - run: | - cd /tmp/build - tar xzf Rocket.Chat.tar.gz - rm Rocket.Chat.tar.gz - - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - - - name: Setup NodeJS - uses: ./.github/actions/setup-node + - uses: ./.github/actions/build-docker with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} node-version: ${{ needs.release-versions.outputs.node-version }} - cache-modules: true - install: true - - - run: yarn build + platform: ${{ matrix.platform }} - - name: Build Docker images - run: | - args=(rocketchat) - - if [[ '${{ matrix.platform }}' = 'alpine' ]]; then - args+=($SERVICES_PUBLISH) - fi; + build-gh-docker: + name: 🚢 Build Docker Images for Production + needs: [build-prod, release-versions] + runs-on: ubuntu-20.04 - docker compose -f docker-compose-ci.yml build "${args[@]}" + env: + RC_DOCKERFILE: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-dockerfile-alpine || needs.release-versions.outputs.rc-dockerfile }} + RC_DOCKER_TAG: ${{ matrix.platform == 'alpine' && needs.release-versions.outputs.rc-docker-tag-alpine || needs.release-versions.outputs.rc-docker-tag }} + DOCKER_TAG: ${{ needs.release-versions.outputs.gh-docker-tag }} + LOWERCASE_REPOSITORY: ${{ needs.release-versions.outputs.lowercase-repo }} + SERVICES_PUBLISH: 'authorization-service account-service ddp-streamer-service presence-service stream-hub-service' - - name: Publish Docker images to GitHub Container Registry - run: | - args=(rocketchat) + strategy: + fail-fast: false + matrix: + platform: ['official', 'alpine'] - if [[ '${{ matrix.platform }}' = 'alpine' ]]; then - args+=($SERVICES_PUBLISH) - fi; + steps: + - uses: actions/checkout@v3 - docker compose -f docker-compose-ci.yml push "${args[@]}" + - uses: ./.github/actions/build-docker + with: + CR_USER: ${{ secrets.CR_USER }} + CR_PAT: ${{ secrets.CR_PAT }} + node-version: ${{ needs.release-versions.outputs.node-version }} + platform: ${{ matrix.platform }} - name: Rename official Docker tag to GitHub Container Registry if: matrix.platform == 'official' @@ -342,7 +252,7 @@ jobs: test-api: name: 🔨 Test API (CE) - needs: [checks, build-gh-docker, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -361,7 +271,7 @@ jobs: test-ui: name: 🔨 Test UI (CE) - needs: [checks, build-gh-docker, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -387,7 +297,7 @@ jobs: test-api-ee: name: 🔨 Test API (EE) - needs: [checks, build-gh-docker, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -409,7 +319,7 @@ jobs: test-ui-ee: name: 🔨 Test UI (EE) - needs: [checks, build-gh-docker, release-versions] + needs: [checks, build-gh-docker-coverage, release-versions] uses: ./.github/workflows/ci-test-e2e.yml with: @@ -449,7 +359,7 @@ jobs: name: 🚀 Publish build and update our registry runs-on: ubuntu-20.04 if: github.event_name == 'release' || github.ref == 'refs/heads/develop' - needs: [tests-done, release-versions] + needs: [build-gh-docker, release-versions] steps: - uses: actions/checkout@v3 diff --git a/apps/meteor/.babelrc b/apps/meteor/.babelrc index a8c20b400ca5..382b93318fab 100644 --- a/apps/meteor/.babelrc +++ b/apps/meteor/.babelrc @@ -1,9 +1,15 @@ { - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ], - "plugins": [ - "babel-plugin-istanbul" - ] + "presets": ["@babel/preset-env", "@babel/preset-react"], + "env": { + "coverage": { + "plugins": [ + [ + "istanbul", + { + "exclude": ["**/*.spec.js", "**/*.test.js"] + } + ] + ] + } + } } From be98f53493e753fb1c3cedefeb8589c2d744ab9e Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:58:28 +0400 Subject: [PATCH 02/17] test: More Business Hour tests surrounding user activation/deactivation (#30343) Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com> --- .../lib/server/functions/getFullUserData.ts | 1 + apps/meteor/tests/data/users.helper.js | 12 +++ apps/meteor/tests/end-to-end/api/01-users.js | 20 ++++- .../api/livechat/19-business-hours.ts | 85 +++++++++++++++++-- 4 files changed, 110 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/getFullUserData.ts b/apps/meteor/app/lib/server/functions/getFullUserData.ts index c04148c07ba8..0703b24d9210 100644 --- a/apps/meteor/app/lib/server/functions/getFullUserData.ts +++ b/apps/meteor/app/lib/server/functions/getFullUserData.ts @@ -21,6 +21,7 @@ const defaultFields = { avatarETag: 1, extension: 1, federated: 1, + statusLivechat: 1, } as const; const fullFields = { diff --git a/apps/meteor/tests/data/users.helper.js b/apps/meteor/tests/data/users.helper.js index 9dac1772dcdc..92425902cb5b 100644 --- a/apps/meteor/tests/data/users.helper.js +++ b/apps/meteor/tests/data/users.helper.js @@ -77,3 +77,15 @@ export const getMe = (overrideCredential = credentials) => resolve(res.body); }); }); + +export const setUserActiveStatus = (userId, activeStatus = true) => + new Promise((resolve) => { + request + .post(api('users.setActiveStatus')) + .set(credentials) + .send({ + userId, + activeStatus, + }) + .end(resolve); + }); diff --git a/apps/meteor/tests/end-to-end/api/01-users.js b/apps/meteor/tests/end-to-end/api/01-users.js index 130f365c96c5..d99fa68a036f 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -23,7 +23,7 @@ import { imgURL } from '../../data/interactions.js'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createRoom } from '../../data/rooms.helper'; import { adminEmail, preferences, password, adminUsername } from '../../data/user'; -import { createUser, login, deleteUser, getUserStatus } from '../../data/users.helper.js'; +import { createUser, login, deleteUser, getUserStatus, getUserByUsername } from '../../data/users.helper.js'; async function createChannel(userCredentials, name) { const res = await request.post(api('channels.create')).set(userCredentials).send({ @@ -3447,6 +3447,24 @@ describe('[Users]', function () { .end(done); }); }); + it('users should retain their roles when they are deactivated', async () => { + const testUser = await createUser({ roles: ['user', 'livechat-agent'] }); + + await request + .post(api('users.setActiveStatus')) + .set(credentials) + .send({ + activeStatus: false, + userId: testUser._id, + }) + .expect('Content-Type', 'application/json') + .expect(200); + + const user = await getUserByUsername(testUser.username); + expect(user).to.have.property('roles'); + expect(user.roles).to.be.an('array').of.length(2); + expect(user.roles).to.include('user', 'livechat-agent'); + }); }); describe('[/users.deactivateIdle]', () => { diff --git a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts index 9085e1cd388d..0585c20bf127 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts @@ -22,12 +22,12 @@ import { getDepartmentById, deleteDepartment, } from '../../../data/livechat/department'; -import { createAgent, makeAgentAvailable } from '../../../data/livechat/rooms'; +import { createAgent, createManager, makeAgentAvailable } from '../../../data/livechat/rooms'; import { removeAgent } from '../../../data/livechat/users'; import { removePermissionFromAllRoles, restorePermissionToRoles, updateSetting, updateEESetting } from '../../../data/permissions.helper'; import type { IUserCredentialsHeader } from '../../../data/user'; import { password } from '../../../data/user'; -import { createUser, deleteUser, getMe, login } from '../../../data/users.helper'; +import { setUserActiveStatus, createUser, deleteUser, getMe, getUserByUsername, login } from '../../../data/users.helper'; import { IS_EE } from '../../../e2e/config/constants'; describe('LIVECHAT - business hours', function () { @@ -249,7 +249,7 @@ describe('LIVECHAT - business hours', function () { }); }); - (IS_EE ? describe : describe.skip)('[EE] BH operations upon creation', () => { + (IS_EE ? describe : describe.skip)('[EE][BH] On Business Hour created', () => { let defaultBusinessHour: ILivechatBusinessHour; before(async () => { @@ -288,7 +288,7 @@ describe('LIVECHAT - business hours', function () { // and "dep2" and both these depts are connected to same BH, then in this case after // archiving "dep1", we'd still need to BH within this user's cache since he's part of // "dep2" which is linked to BH - (IS_EE ? describe : describe.skip)('[EE] BH operations post department archiving', () => { + (IS_EE ? describe : describe.skip)('[EE][BH] On Department archived', () => { let defaultBusinessHour: ILivechatBusinessHour; let customBusinessHour: ILivechatBusinessHour; let deptLinkedToCustomBH: ILivechatDepartment; @@ -434,7 +434,7 @@ describe('LIVECHAT - business hours', function () { await deleteUser(agentLinkedToDept.user); }); }); - (IS_EE ? describe : describe.skip)('[EE] BH operations post department disablement', () => { + (IS_EE ? describe : describe.skip)('[EE][BH] On Department disabled', () => { let defaultBusinessHour: ILivechatBusinessHour; let customBusinessHour: ILivechatBusinessHour; let deptLinkedToCustomBH: ILivechatDepartment; @@ -578,7 +578,7 @@ describe('LIVECHAT - business hours', function () { await deleteUser(agentLinkedToDept.user); }); }); - (IS_EE ? describe : describe.skip)('[EE] BH operations post department removal', () => { + (IS_EE ? describe : describe.skip)('[EE][BH] On Department removed', () => { let defaultBusinessHour: ILivechatBusinessHour; let customBusinessHour: ILivechatBusinessHour; let deptLinkedToCustomBH: ILivechatDepartment; @@ -702,7 +702,7 @@ describe('LIVECHAT - business hours', function () { await deleteUser(agentLinkedToDept.user); }); }); - describe('BH behavior upon new agent creation/deletion', () => { + describe('[CE][BH] On Agent created/removed', () => { let defaultBH: ILivechatBusinessHour; let agent: ILivechatAgent; let agentCredentials: IUserCredentialsHeader; @@ -782,4 +782,75 @@ describe('LIVECHAT - business hours', function () { await deleteUser(agent._id); }); }); + + describe('[CE][BH] On Agent deactivated/activated', () => { + let defaultBH: ILivechatBusinessHour; + let agent: ILivechatAgent; + + before(async () => { + await updateSetting('Livechat_enable_business_hours', true); + await updateEESetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.SINGLE); + // wait for callbacks to run + await sleep(2000); + + defaultBH = await getDefaultBusinessHour(); + await openOrCloseBusinessHour(defaultBH, true); + + agent = await createUser(); + await createAgent(agent.username); + }); + + after(async () => { + await deleteUser(agent); + await updateSetting('Livechat_enable_business_hours', false); + }); + + it('should verify if agent becomes unavailable to take chats when user is deactivated', async () => { + await setUserActiveStatus(agent._id, false); + + const latestAgent = await getUserByUsername(agent.username); + + expect(latestAgent).to.be.an('object'); + expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.NOT_AVAILABLE); + }); + + it('should verify if agent becomes available to take chats when user is activated, if business hour is active', async () => { + await openOrCloseBusinessHour(defaultBH, true); + + await setUserActiveStatus(agent._id, true); + + const latestAgent = await getUserByUsername(agent.username); + + expect(latestAgent).to.be.an('object'); + expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.AVAILABLE); + }); + it('should verify if agent becomes unavailable to take chats when user is activated, if business hour is inactive', async () => { + await openOrCloseBusinessHour(defaultBH, false); + + await setUserActiveStatus(agent._id, false); + await setUserActiveStatus(agent._id, true); + + const latestAgent = await getUserByUsername(agent.username); + + expect(latestAgent).to.be.an('object'); + expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.NOT_AVAILABLE); + }); + it('should verify if managers are not able to make deactivated agents available', async () => { + await createManager(); + + await setUserActiveStatus(agent._id, false); + + const response = await request + .post(api('livechat/agent.status')) + .set(credentials) + .send({ + status: 'available', + agentId: agent._id, + }) + .expect(400); + + expect(response.body).to.have.property('success', false); + expect(response.body).to.have.property('error', 'error-user-deactivated'); + }); + }); }); From bb3a0d4222061f629c97933d2ab6135be356d04d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 11 Sep 2023 17:41:24 -0300 Subject: [PATCH 03/17] chore: add subscription metrics to ddp-streamer (#30289) --- ee/apps/ddp-streamer/src/DDPStreamer.ts | 11 +++++++++++ ee/apps/ddp-streamer/src/Server.ts | 11 +++++++++++ packages/core-services/src/types/IBroker.ts | 1 + 3 files changed, 23 insertions(+) diff --git a/ee/apps/ddp-streamer/src/DDPStreamer.ts b/ee/apps/ddp-streamer/src/DDPStreamer.ts index 868ad8fec202..bccb35d2b326 100644 --- a/ee/apps/ddp-streamer/src/DDPStreamer.ts +++ b/ee/apps/ddp-streamer/src/DDPStreamer.ts @@ -72,6 +72,15 @@ export class DDPStreamer extends ServiceClass { return; } + metrics.register({ + name: 'rocketchat_subscription', + type: 'histogram', + labelNames: ['subscription'], + description: 'Client subscriptions to Rocket.Chat', + unit: 'millisecond', + quantiles: true, + }); + metrics.register({ name: 'users_connected', type: 'gauge', @@ -86,6 +95,8 @@ export class DDPStreamer extends ServiceClass { description: 'Users logged by streamer', }); + server.setMetrics(metrics); + server.on(DDP_EVENTS.CONNECTED, () => { metrics.increment('users_connected', { nodeID }, 1); }); diff --git a/ee/apps/ddp-streamer/src/Server.ts b/ee/apps/ddp-streamer/src/Server.ts index 01c7c63511ff..af083621230d 100644 --- a/ee/apps/ddp-streamer/src/Server.ts +++ b/ee/apps/ddp-streamer/src/Server.ts @@ -1,5 +1,6 @@ import { EventEmitter } from 'events'; +import type { IServiceMetrics } from '@rocket.chat/core-services'; import { MeteorService, isMeteorError, MeteorError } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import ejson from 'ejson'; @@ -38,6 +39,8 @@ export class Server extends EventEmitter { private _methods = new Map(); + private metrics?: IServiceMetrics; + public readonly id = uuidv1(); serialize = ejson.stringify; @@ -52,6 +55,10 @@ export class Server extends EventEmitter { return ejson.parse(payload); }; + setMetrics(metrics: IServiceMetrics): void { + this.metrics = metrics; + } + async call(client: Client, packet: IPacket): Promise { // if client is not connected we don't need to do anything if (client.ws.readyState !== WebSocket.OPEN) { @@ -103,9 +110,13 @@ export class Server extends EventEmitter { throw new MeteorError(404, `Subscription '${packet.name}' not found`); } + const end = this.metrics?.timer('rocketchat_subscription', { subscription: packet.name }); + const publication = new Publication(client, packet, this); const [eventName, options] = packet.params; await fn.call(publication, eventName, options); + + end?.(); } catch (err: unknown) { return this.nosub(client, packet, handleInternalException(err, 'Subscription error')); } diff --git a/packages/core-services/src/types/IBroker.ts b/packages/core-services/src/types/IBroker.ts index 8647d04a56dc..4bd48afef0ff 100644 --- a/packages/core-services/src/types/IBroker.ts +++ b/packages/core-services/src/types/IBroker.ts @@ -27,6 +27,7 @@ export type BaseMetricOptions = { labelNames?: Array; unit?: string; aggregator?: string; + [key: string]: unknown; }; export interface IServiceMetrics { From 258e9b338172b24066c8baa825ed56085ab05194 Mon Sep 17 00:00:00 2001 From: Heitor Tanoue <68477006+heitortanoue@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:57:53 -0300 Subject: [PATCH 04/17] chore: move team mentions to ce (#30175) Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> --- .../server/getMentionedTeamMembers.ts | 25 ++++++++++++ apps/meteor/app/mentions/server/index.ts | 3 +- apps/meteor/app/mentions/server/server.ts | 24 +++++++++--- apps/meteor/ee/app/license/server/bundles.ts | 2 - .../teams-mention/server/EEMentionQueries.js | 19 --------- .../app/teams-mention/server/EESpotlight.js | 34 ---------------- .../ee/app/teams-mention/server/index.ts | 39 ------------------- apps/meteor/ee/server/index.ts | 1 - apps/meteor/server/lib/spotlight.js | 30 ++++++++++++-- 9 files changed, 72 insertions(+), 105 deletions(-) create mode 100644 apps/meteor/app/mentions/server/getMentionedTeamMembers.ts delete mode 100644 apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js delete mode 100644 apps/meteor/ee/app/teams-mention/server/EESpotlight.js delete mode 100644 apps/meteor/ee/app/teams-mention/server/index.ts diff --git a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts new file mode 100644 index 000000000000..1d36400296b4 --- /dev/null +++ b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts @@ -0,0 +1,25 @@ +import { Team } from '@rocket.chat/core-services'; +import type { IMessage } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../lib/callbacks'; + +interface IExtraDataForNotification { + userMentions: any[]; + otherMentions: any[]; + message: IMessage; +} + +callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDataForNotification) => { + const { otherMentions } = extra ?? {}; + + const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); + + if (!teamIds?.length) { + return mentionIds; + } + + const members = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); + mentionIds.push(...new Set(members.map(({ userId }) => userId).filter((userId) => !mentionIds.includes(userId)))); + + return mentionIds; +}); diff --git a/apps/meteor/app/mentions/server/index.ts b/apps/meteor/app/mentions/server/index.ts index 474d41a439e1..a04af05b9db1 100644 --- a/apps/meteor/app/mentions/server/index.ts +++ b/apps/meteor/app/mentions/server/index.ts @@ -1,2 +1,3 @@ -import './server'; +import './getMentionedTeamMembers'; import './methods/getUserMentionsByChannel'; +import './server'; diff --git a/apps/meteor/app/mentions/server/server.ts b/apps/meteor/app/mentions/server/server.ts index 5eb70aae4656..a5b0f89526a2 100644 --- a/apps/meteor/app/mentions/server/server.ts +++ b/apps/meteor/app/mentions/server/server.ts @@ -1,5 +1,5 @@ -import { api } from '@rocket.chat/core-services'; -import type { IUser, IRoom } from '@rocket.chat/core-typings'; +import { api, Team } from '@rocket.chat/core-services'; +import type { IUser, IRoom, ITeam } from '@rocket.chat/core-typings'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -9,16 +9,28 @@ import { settings } from '../../settings/server'; import MentionsServer from './Mentions'; export class MentionQueries { - async getUsers(usernames: string[]): Promise<(Pick & { type: 'user' })[]> { + async getUsers( + usernames: string[], + ): Promise<((Pick & { type: 'user' }) | (Pick & { type: 'team' }))[]> { + const uniqueUsernames = [...new Set(usernames)]; + const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); + const users = await Users.find( - { username: { $in: [...new Set(usernames)] } }, + { username: { $in: uniqueUsernames } }, { projection: { _id: true, username: true, name: 1 } }, ).toArray(); - return users.map((user) => ({ + const taggedUsers = users.map((user) => ({ ...user, - type: 'user', + type: 'user' as const, + })); + + const taggedTeams = teams.map((team) => ({ + ...team, + type: 'team' as const, })); + + return [...taggedUsers, ...taggedTeams]; } async getUser(userId: string): Promise { diff --git a/apps/meteor/ee/app/license/server/bundles.ts b/apps/meteor/ee/app/license/server/bundles.ts index 507283b3e60f..70f9d7b5a653 100644 --- a/apps/meteor/ee/app/license/server/bundles.ts +++ b/apps/meteor/ee/app/license/server/bundles.ts @@ -8,7 +8,6 @@ export type BundleFeature = | 'engagement-dashboard' | 'push-privacy' | 'scalability' - | 'teams-mention' | 'saml-enterprise' | 'device-management' | 'oauth-enterprise' @@ -32,7 +31,6 @@ const bundles: IBundle = { 'engagement-dashboard', 'push-privacy', 'scalability', - 'teams-mention', 'saml-enterprise', 'oauth-enterprise', 'device-management', diff --git a/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js b/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js deleted file mode 100644 index 020a90365d26..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/EEMentionQueries.js +++ /dev/null @@ -1,19 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; - -export const MentionQueriesEnterprise = { - async getUsers(sup, usernames) { - const uniqueUsernames = [...new Set(usernames)]; - const teams = await Team.listByNames(uniqueUsernames, { projection: { name: 1 } }); - - if (!teams?.length) { - return sup(usernames); - } - - return teams - .map((team) => ({ - ...team, - type: 'team', - })) - .concat(sup(usernames)); - }, -}; diff --git a/apps/meteor/ee/app/teams-mention/server/EESpotlight.js b/apps/meteor/ee/app/teams-mention/server/EESpotlight.js deleted file mode 100644 index 83829a759bba..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/EESpotlight.js +++ /dev/null @@ -1,34 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; - -export const SpotlightEnterprise = { - mapTeams(_, teams) { - return teams.map((t) => { - t.isTeam = true; - t.username = t.name; - t.status = 'online'; - return t; - }); - }, - - async _searchTeams(_, userId, { text, options, users, mentions }) { - if (!mentions) { - return users; - } - - options.limit -= users.length; - - if (options.limit <= 0) { - return users; - } - - const teamOptions = { ...options, projection: { name: 1, type: 1 } }; - const teams = await Team.search(userId, text, teamOptions); - users.push(...this.mapTeams(teams)); - - return users; - }, - - async _performExtraUserSearches(_, userId, searchParams) { - return this._searchTeams(userId, searchParams); - }, -}; diff --git a/apps/meteor/ee/app/teams-mention/server/index.ts b/apps/meteor/ee/app/teams-mention/server/index.ts deleted file mode 100644 index 631610e4c6d8..000000000000 --- a/apps/meteor/ee/app/teams-mention/server/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Team } from '@rocket.chat/core-services'; -import type { ITeamMember, IMessage } from '@rocket.chat/core-typings'; - -import { MentionQueries } from '../../../../app/mentions/server/server'; -import { callbacks } from '../../../../lib/callbacks'; -import { Spotlight } from '../../../../server/lib/spotlight'; -import { onLicense } from '../../license/server'; -import { overwriteClassOnLicense } from '../../license/server/license'; -import { MentionQueriesEnterprise } from './EEMentionQueries'; -import { SpotlightEnterprise } from './EESpotlight'; - -interface IExtraDataForNotification { - userMentions: any[]; - otherMentions: any[]; - message: IMessage; -} - -await onLicense('teams-mention', async () => { - // Override spotlight with EE version - await overwriteClassOnLicense('teams-mention', Spotlight, SpotlightEnterprise); - await overwriteClassOnLicense('teams-mention', MentionQueries, MentionQueriesEnterprise); - - callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDataForNotification) => { - const { otherMentions } = extra ?? {}; - - const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); - - if (!teamIds?.length) { - return mentionIds; - } - - const members: ITeamMember[] = await Team.getMembersByTeamIds(teamIds, { projection: { userId: 1 } }); - mentionIds.push( - ...new Set(members.map(({ userId }: { userId: string }) => userId).filter((userId: string) => !mentionIds.includes(userId))), - ); - - return mentionIds; - }); -}); diff --git a/apps/meteor/ee/server/index.ts b/apps/meteor/ee/server/index.ts index 2e526776c772..9b56239ad046 100644 --- a/apps/meteor/ee/server/index.ts +++ b/apps/meteor/ee/server/index.ts @@ -8,7 +8,6 @@ import '../app/livechat-enterprise/server/index'; import '../app/message-read-receipt/server/index'; import '../app/voip-enterprise/server/index'; import '../app/settings/server/index'; -import '../app/teams-mention/server/index'; import './api'; import './requestSeatsRoute'; import './configuration/index'; diff --git a/apps/meteor/server/lib/spotlight.js b/apps/meteor/server/lib/spotlight.js index 38dc1b873878..ae38c903cbe2 100644 --- a/apps/meteor/server/lib/spotlight.js +++ b/apps/meteor/server/lib/spotlight.js @@ -1,3 +1,4 @@ +import { Team } from '@rocket.chat/core-services'; import { Users, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; @@ -133,8 +134,31 @@ export class Spotlight { } } - async _performExtraUserSearches(/* userId, searchParams */) { - // Overwrite this method to include extra searches + mapTeams(teams) { + return teams.map((t) => { + t.isTeam = true; + t.username = t.name; + t.status = 'online'; + return t; + }); + } + + async _searchTeams(userId, { text, options, users, mentions }) { + if (!mentions) { + return users; + } + + options.limit -= users.length; + + if (options.limit <= 0) { + return users; + } + + const teamOptions = { ...options, projection: { name: 1, type: 1 } }; + const teams = await Team.search(userId, text, teamOptions); + users.push(...this.mapTeams(teams)); + + return users; } async searchUsers({ userId, rid, text, usernames, mentions }) { @@ -245,7 +269,7 @@ export class Spotlight { return users; } - if (await this._performExtraUserSearches(userId, searchParams)) { + if (await this._searchTeams(userId, searchParams)) { return users; } From f556518fa1300b91bb5b035e463d80ff8556ce55 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 11 Sep 2023 21:24:29 -0300 Subject: [PATCH 05/17] fix: Consider only sessions from last few days for SAU/MAU (#30354) --- .changeset/strong-laws-pump.md | 8 + .../app/statistics/server/lib/SAUMonitor.ts | 38 +--- apps/meteor/server/models/raw/Sessions.ts | 205 +++++++++--------- .../src/models/ISessionsModel.ts | 4 + 4 files changed, 129 insertions(+), 126 deletions(-) create mode 100644 .changeset/strong-laws-pump.md diff --git a/.changeset/strong-laws-pump.md b/.changeset/strong-laws-pump.md new file mode 100644 index 000000000000..a4afefd65316 --- /dev/null +++ b/.changeset/strong-laws-pump.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/model-typings': patch +'@rocket.chat/meteor': patch +--- + +Change SAU aggregation to consider only sessions from few days ago instead of the whole past. + +This is particularly important for large workspaces in case the cron job did not run for some time, in that case the amount of sessions would accumulate and the aggregation would take a long time to run. diff --git a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts index 79ce688cffd3..b3aa68337106 100644 --- a/apps/meteor/app/statistics/server/lib/SAUMonitor.ts +++ b/apps/meteor/app/statistics/server/lib/SAUMonitor.ts @@ -318,33 +318,19 @@ export class SAUMonitorClass { return; } - logger.info('[aggregate] - Aggregating data.'); - - const date = new Date(); - date.setDate(date.getDate() - 0); // yesterday - const yesterday = getDateObj(date); - - for await (const record of aggregates.dailySessionsOfYesterday(Sessions.col, yesterday)) { - await Sessions.updateOne( - { _id: `${record.userId}-${record.year}-${record.month}-${record.day}` }, - { $set: record }, - { upsert: true }, - ); + const today = new Date(); + + // get sessions from 3 days ago to make sure even if a few cron jobs were skipped, we still have the data + const threeDaysAgo = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 3, 0, 0, 0, 0); + + const period = { start: getDateObj(threeDaysAgo), end: getDateObj(today) }; + + logger.info({ msg: '[aggregate] - Aggregating data.', period }); + + for await (const record of aggregates.dailySessions(Sessions.col, period)) { + await Sessions.updateDailySessionById(`${record.userId}-${record.year}-${record.month}-${record.day}`, record); } - await Sessions.updateMany( - { - type: 'session', - year: { $lte: yesterday.year }, - month: { $lte: yesterday.month }, - day: { $lte: yesterday.day }, - }, - { - $set: { - type: 'computed-session', - _computedAt: new Date(), - }, - }, - ); + await Sessions.updateAllSessionsByDateToComputed(period); } } diff --git a/apps/meteor/server/models/raw/Sessions.ts b/apps/meteor/server/models/raw/Sessions.ts index 68d5149232ed..c02fc8b5de99 100644 --- a/apps/meteor/server/models/raw/Sessions.ts +++ b/apps/meteor/server/models/raw/Sessions.ts @@ -167,9 +167,9 @@ const getProjectionByFullDate = (): { day: string; month: string; year: string } }); export const aggregates = { - dailySessionsOfYesterday( + dailySessions( collection: Collection, - { year, month, day }: DestructuredDate, + { start, end }: DestructuredRange, ): AggregationCursor< Pick & { time: number; @@ -178,115 +178,101 @@ export const aggregates = { _computedAt: string; } > { - return collection.aggregate< - Pick & { - time: number; - sessions: number; - devices: ISession['device'][]; - _computedAt: string; - } - >( - [ - { - $match: { - userId: { $exists: true }, - lastActivityAt: { $exists: true }, - device: { $exists: true }, - type: 'session', - $or: [ - { - year: { $lt: year }, - }, - { - year, - month: { $lt: month }, - }, - { - year, - month, - day: { $lte: day }, - }, - ], - }, - }, - { - $project: { - userId: 1, - device: 1, - day: 1, - month: 1, - year: 1, - mostImportantRole: 1, - time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } }, - }, - }, - { - $match: { - time: { $gt: 0 }, - }, + const pipeline = [ + { + $match: { + userId: { $exists: true }, + lastActivityAt: { $exists: true }, + device: { $exists: true }, + type: 'session', + ...matchBasedOnDate(start, end), }, - { - $group: { - _id: { - userId: '$userId', - device: '$device', - day: '$day', - month: '$month', - year: '$year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: 1 }, - }, + }, + { + $project: { + userId: 1, + device: 1, + day: 1, + month: 1, + year: 1, + mostImportantRole: 1, + time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } }, }, - { - $sort: { - time: -1, - }, + }, + { + $match: { + time: { $gt: 0 }, }, - { - $group: { - _id: { - userId: '$_id.userId', - day: '$_id.day', - month: '$_id.month', - year: '$_id.year', - }, - mostImportantRole: { $first: '$mostImportantRole' }, - time: { $sum: '$time' }, - sessions: { $sum: '$sessions' }, - devices: { - $push: { - sessions: '$sessions', - time: '$time', - device: '$_id.device', - }, - }, + }, + { + $group: { + _id: { + userId: '$userId', + device: '$device', + day: '$day', + month: '$month', + year: '$year', }, + mostImportantRole: { $first: '$mostImportantRole' }, + time: { $sum: '$time' }, + sessions: { $sum: 1 }, }, - { - $sort: { - _id: 1, - }, + }, + { + $sort: { + time: -1, }, - { - $project: { - _id: 0, - type: { $literal: 'user_daily' }, - _computedAt: { $literal: new Date() }, + }, + { + $group: { + _id: { + userId: '$_id.userId', day: '$_id.day', month: '$_id.month', year: '$_id.year', - userId: '$_id.userId', - mostImportantRole: 1, - time: 1, - sessions: 1, - devices: 1, + }, + mostImportantRole: { $first: '$mostImportantRole' }, + time: { $sum: '$time' }, + sessions: { $sum: '$sessions' }, + devices: { + $push: { + sessions: '$sessions', + time: '$time', + device: '$_id.device', + }, }, }, - ], - { allowDiskUse: true }, - ); + }, + { + $sort: { + _id: 1, + }, + }, + { + $project: { + _id: 0, + type: { $literal: 'user_daily' }, + _computedAt: { $literal: new Date() }, + day: '$_id.day', + month: '$_id.month', + year: '$_id.year', + userId: '$_id.userId', + mostImportantRole: 1, + time: 1, + sessions: 1, + devices: 1, + }, + }, + ]; + + return collection.aggregate< + Pick & { + time: number; + sessions: number; + devices: ISession['device'][]; + _computedAt: string; + } + >(pipeline, { allowDiskUse: true }); }, async getUniqueUsersOfYesterday( @@ -1616,4 +1602,23 @@ export class SessionsRaw extends BaseRaw implements ISessionsModel { return this.col.bulkWrite(ops, { ordered: false }); } + + async updateDailySessionById(_id: ISession['_id'], record: Partial): Promise { + return this.updateOne({ _id }, { $set: record }, { upsert: true }); + } + + async updateAllSessionsByDateToComputed({ start, end }: DestructuredRange): Promise { + return this.updateMany( + { + type: 'session', + ...matchBasedOnDate(start, end), + }, + { + $set: { + type: 'computed-session', + _computedAt: new Date(), + }, + }, + ); + } } diff --git a/packages/model-typings/src/models/ISessionsModel.ts b/packages/model-typings/src/models/ISessionsModel.ts index cebe0c861d3f..1e6a36fd6f78 100644 --- a/packages/model-typings/src/models/ISessionsModel.ts +++ b/packages/model-typings/src/models/ISessionsModel.ts @@ -145,4 +145,8 @@ export interface ISessionsModel extends IBaseModel { }): Promise; createBatch(sessions: OptionalId[]): Promise; + + updateDailySessionById(_id: ISession['_id'], record: Partial): Promise; + + updateAllSessionsByDateToComputed({ start, end }: DestructuredRange): Promise; } From f37e404cb3226b3971e305722af1d75097790e82 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 12 Sep 2023 06:05:48 +0530 Subject: [PATCH 06/17] chore: bump fuselage (#30357) --- yarn.lock | 88 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 249995955936..100b3ba60fe7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7928,6 +7928,19 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/css-in-js@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/css-in-js@npm:0.31.26-dev.23" + dependencies: + "@emotion/hash": ^0.9.0 + "@rocket.chat/css-supports": ~0.31.26-dev.23 + "@rocket.chat/memo": ~0.31.26-dev.23 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.26-dev.23 + stylis: ~4.1.3 + checksum: 6d71bd0f232c8ea3fc2711347064ddd14925b1c2b8713f6d7649b98679455029a53ee41d08b98d010da3ea4789afa21a15901a92efef61dee7b32d6965157445 + languageName: node + linkType: hard + "@rocket.chat/css-supports@npm:~0.31.26-dev.19": version: 0.31.26-dev.19 resolution: "@rocket.chat/css-supports@npm:0.31.26-dev.19" @@ -7937,6 +7950,15 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/css-supports@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/css-supports@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/memo": ~0.31.26-dev.23 + checksum: a4f25562df67214b1c92c85a1cd16eb03fc2aea385f48cdde42ad0053b9e03a92ca9e3486d1387c7a31cf68f47fa888825f31acae8f4700ee2b9f03495286a12 + languageName: node + linkType: hard + "@rocket.chat/ddp-client@workspace:^, @rocket.chat/ddp-client@workspace:ee/packages/ddp-client": version: 0.0.0-use.local resolution: "@rocket.chat/ddp-client@workspace:ee/packages/ddp-client" @@ -8142,13 +8164,20 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.379": +"@rocket.chat/fuselage-tokens@npm:next": version: 0.32.0-dev.379 resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.379" checksum: c5cf40295c4ae1a5918651b9e156629d6400d5823b8cf5f81a14c66da986a9302d79392b45e991c2fc37aad9633f3d8e2f7f29c68969592340b05968265244e6 languageName: node linkType: hard +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.383": + version: 0.32.0-dev.383 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.383" + checksum: bd3504fa6a7ce4ed6fc91246c4c8a4e3e3da8bc5e2c5590e7f913bc1fd6f08896aa4a6c4b1d01dccf78267ade9ad5a831c788cb17a4eb744deefb45032a34894 + languageName: node + linkType: hard + "@rocket.chat/fuselage-ui-kit@workspace:^, @rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit, @rocket.chat/fuselage-ui-kit@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/fuselage-ui-kit@workspace:packages/fuselage-ui-kit" @@ -8195,9 +8224,9 @@ __metadata: "@rocket.chat/icons": "*" "@rocket.chat/prettier-config": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 "@rocket.chat/ui-kit": "*" - "@rocket.chat/ui-video-conf": 1.0.3 + "@rocket.chat/ui-video-conf": 1.0.4 "@tanstack/react-query": "*" react: "*" react-dom: "*" @@ -8205,14 +8234,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.429 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.429" - dependencies: - "@rocket.chat/css-in-js": ~0.31.26-dev.19 - "@rocket.chat/css-supports": ~0.31.26-dev.19 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.379 - "@rocket.chat/memo": ~0.31.26-dev.19 - "@rocket.chat/styled": ~0.31.26-dev.19 + version: 0.32.0-dev.433 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.433" + dependencies: + "@rocket.chat/css-in-js": ~0.31.26-dev.23 + "@rocket.chat/css-supports": ~0.31.26-dev.23 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.383 + "@rocket.chat/memo": ~0.31.26-dev.23 + "@rocket.chat/styled": ~0.31.26-dev.23 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -8224,7 +8253,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 13ea95dea15c3677f82ffeb50780bc3479512cba6e226080bf464cf876794ed267db3419c45f63ddeaaff6a3401426ca4722e23e0f3586ca4f8eb2e6e25a7a70 + checksum: 2696da3e5cdf9d21c9c96ba069a3ef44b946ce832796ed10047666da780ee3ae88d679f3c3222fe6c6d88b1bdc0b7dab97c83d0acfbb67ca330a14b6e4739aa9 languageName: node linkType: hard @@ -8279,7 +8308,7 @@ __metadata: ts-jest: ~29.0.5 typescript: ~5.2.2 peerDependencies: - "@rocket.chat/core-typings": 6.3.3 + "@rocket.chat/core-typings": 6.3.4 "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-tokens": "*" @@ -8482,6 +8511,13 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/memo@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/memo@npm:0.31.26-dev.23" + checksum: 68301161d87ba25347f1d2ab85c139ba86c5fdd1101f41678808c19ba461772814f4bff048a30e4aefd08978fe2feb952c541bddc0beb6bc3cd190bd7852393b + languageName: node + linkType: hard + "@rocket.chat/message-parser@npm:next": version: 0.32.0-dev.377 resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.377" @@ -9312,6 +9348,15 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/styled@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/styled@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/css-in-js": ~0.31.26-dev.23 + checksum: 0a1ff89b068f011097671c617844856b91f2477c16ff3771fcfc0bab62a905a9b21c7b79549ff028613700a72685fd591ba9cbeda6b5d3bd8becd3af7aef0498 + languageName: node + linkType: hard + "@rocket.chat/stylis-logical-props-middleware@npm:~0.31.26-dev.19": version: 0.31.26-dev.19 resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.26-dev.19" @@ -9323,6 +9368,17 @@ __metadata: languageName: node linkType: hard +"@rocket.chat/stylis-logical-props-middleware@npm:~0.31.26-dev.23": + version: 0.31.26-dev.23 + resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.26-dev.23" + dependencies: + "@rocket.chat/css-supports": ~0.31.26-dev.23 + peerDependencies: + stylis: 4.0.10 + checksum: b2fbfad3b2f4dedd9023b30d4cdc51e76ae76faeeca5819cf697e896c02fd4bb2dde5bbc428b377d77f32011fd8cc82c6d98a84d66b93056ef981c13aee1dc67 + languageName: node + linkType: hard + "@rocket.chat/tools@workspace:^, @rocket.chat/tools@workspace:packages/tools": version: 0.0.0-use.local resolution: "@rocket.chat/tools@workspace:packages/tools" @@ -9382,7 +9438,7 @@ __metadata: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 react: ~17.0.2 languageName: unknown linkType: soft @@ -9534,7 +9590,7 @@ __metadata: "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/icons": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 react: ^17.0.2 react-dom: ^17.0.2 languageName: unknown @@ -9618,7 +9674,7 @@ __metadata: typescript: ~5.2.2 peerDependencies: "@rocket.chat/layout": "*" - "@rocket.chat/ui-contexts": 1.0.3 + "@rocket.chat/ui-contexts": 1.0.4 "@tanstack/react-query": "*" react: "*" react-hook-form: "*" From afd2b0d974595198c63e28b8aacc8cb774c4e936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= Date: Tue, 12 Sep 2023 00:18:12 -0300 Subject: [PATCH 07/17] regression: Rooms page filters faulty behavior (#30315) --- .../client/views/admin/rooms/RoomRow.tsx | 90 ++++++++ .../client/views/admin/rooms/RoomsTable.tsx | 200 ++++-------------- .../views/admin/rooms/RoomsTableFilters.tsx | 46 +--- .../admin/rooms/useFilteredTypeRooms.tsx | 31 --- .../rooms/useFilteredVisibilityRooms.tsx | 24 --- .../rocketchat-i18n/i18n/en.i18n.json | 7 +- 6 files changed, 144 insertions(+), 254 deletions(-) create mode 100644 apps/meteor/client/views/admin/rooms/RoomRow.tsx delete mode 100644 apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx delete mode 100644 apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx diff --git a/apps/meteor/client/views/admin/rooms/RoomRow.tsx b/apps/meteor/client/views/admin/rooms/RoomRow.tsx new file mode 100644 index 000000000000..2c0dbb8c31a6 --- /dev/null +++ b/apps/meteor/client/views/admin/rooms/RoomRow.tsx @@ -0,0 +1,90 @@ +import { isDiscussion } from '@rocket.chat/core-typings'; +import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; +import { Box, Icon } from '@rocket.chat/fuselage'; +import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useCallback } from 'react'; + +import { GenericTableCell, GenericTableRow } from '../../../components/GenericTable'; +import RoomAvatar from '../../../components/avatar/RoomAvatar'; +import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; + +const roomTypeI18nMap = { + l: 'Omnichannel', + c: 'Channel', + d: 'Direct_Message', + p: 'Private_Channel', +} as const; + +const getRoomDisplayName = (room: Pick): string | undefined => + room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room); + +const RoomRow = ({ room }: { room: Pick }) => { + const t = useTranslation(); + const mediaQuery = useMediaQuery('(min-width: 1024px)'); + const router = useRouter(); + + const { _id, t: type, usersCount, msgs, default: isDefault, featured, ...args } = room; + const icon = roomCoordinator.getRoomDirectives(room.t).getIcon?.(room); + const roomName = getRoomDisplayName(room); + + const getRoomType = ( + room: Pick, + ): (typeof roomTypeI18nMap)[keyof typeof roomTypeI18nMap] | 'Teams_Public_Team' | 'Teams_Private_Team' | 'Discussion' => { + if (room.teamMain) { + return room.t === 'c' ? 'Teams_Public_Team' : 'Teams_Private_Team'; + } + if (isDiscussion(room)) { + return 'Discussion'; + } + return roomTypeI18nMap[(room as IRoom).t as keyof typeof roomTypeI18nMap]; + }; + + const onClick = useCallback( + (rid) => (): void => + router.navigate({ + name: 'admin-rooms', + params: { + context: 'edit', + id: rid, + }, + }), + [router], + ); + + return ( + + + + + + {icon && } + + {roomName} + + + + + + + {t(getRoomType(room))} + + + {usersCount} + {mediaQuery && {msgs}} + {mediaQuery && {isDefault ? t('True') : t('False')}} + {mediaQuery && {featured ? t('True') : t('False')}} + + ); +}; + +export default RoomRow; diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 6b2c4435c8fa..c480fab9f657 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -1,31 +1,23 @@ -import { type IRoom, isDiscussion, isPublicRoom } from '@rocket.chat/core-typings'; -import { Box, Icon, Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import type { OptionProp } from '@rocket.chat/ui-client'; -import { useEndpoint, useRouter, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import type { CSSProperties, ReactElement, MutableRefObject } from 'react'; -import React, { useRef, useState, useEffect, useMemo, useCallback } from 'react'; +import type { ReactElement, MutableRefObject } from 'react'; +import React, { useRef, useState, useEffect, useMemo } from 'react'; import GenericNoResults from '../../../components/GenericNoResults'; import { GenericTable, GenericTableBody, - GenericTableCell, GenericTableHeader, GenericTableHeaderCell, GenericTableLoadingTable, - GenericTableRow, } from '../../../components/GenericTable'; import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; import { useSort } from '../../../components/GenericTable/hooks/useSort'; -import RoomAvatar from '../../../components/avatar/RoomAvatar'; -import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; +import RoomRow from './RoomRow'; import RoomsTableFilters from './RoomsTableFilters'; -import { useFilteredTypeRooms } from './useFilteredTypeRooms'; -import { useFilteredVisibilityRooms } from './useFilteredVisibilityRooms'; - -const style: CSSProperties = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; type RoomFilters = { searchText: string; @@ -33,34 +25,9 @@ type RoomFilters = { visibility: OptionProp[]; }; -const DEFAULT_TYPES = ['d', 'p', 'c', 'l', 'discussions', 'teams']; - -const roomTypeI18nMap = { - l: 'Omnichannel', - c: 'Channel', - d: 'Direct_Message', - p: 'Private_Channel', -} as const; - -const getRoomType = ( - room: IRoom, -): (typeof roomTypeI18nMap)[keyof typeof roomTypeI18nMap] | 'Teams_Public_Team' | 'Teams_Private_Team' | 'Discussion' => { - if (room.teamMain) { - return room.t === 'c' ? 'Teams_Public_Team' : 'Teams_Private_Team'; - } - if (isDiscussion(room)) { - return 'Discussion'; - } - return roomTypeI18nMap[(room as IRoom).t as keyof typeof roomTypeI18nMap]; -}; - -const getRoomDisplayName = (room: IRoom): string | undefined => - room.t === 'd' ? room.usernames?.join(' x ') : roomCoordinator.getRoomName(room.t, room); - const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): ReactElement => { - const mediaQuery = useMediaQuery('(min-width: 1024px)'); - const t = useTranslation(); + const mediaQuery = useMediaQuery('(min-width: 1024px)'); const [roomFilters, setRoomFilters] = useState({ searchText: '', types: [], visibility: [] }); @@ -80,29 +47,15 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`, count: itemsPerPage, offset: searchText === prevRoomFilterText.current ? current : 0, - types: DEFAULT_TYPES, + types: [...roomFilters.types.map((roomType) => roomType.id)], }; - }, [searchText, sortBy, sortDirection, itemsPerPage, prevRoomFilterText, current, setCurrent]), + }, [searchText, sortBy, sortDirection, itemsPerPage, current, roomFilters.types, setCurrent]), 500, ); const getAdminRooms = useEndpoint('GET', '/v1/rooms.adminRooms'); - const dispatchToastMessage = useToastMessageDispatch(); - - const { data, refetch, isSuccess, isLoading, isError } = useQuery( - ['rooms', query, 'admin'], - async () => { - const adminRooms = await getAdminRooms(query); - - return { ...adminRooms, rooms: adminRooms.rooms as IRoom[] }; - }, - { - onError: (error) => { - dispatchToastMessage({ type: 'error', message: error }); - }, - }, - ); + const { data, refetch, isSuccess, isLoading, isError } = useQuery(['rooms', query, 'admin'], async () => getAdminRooms(query)); useEffect(() => { reload.current = refetch; @@ -112,48 +65,29 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React prevRoomFilterText.current = searchText; }, [searchText]); - const router = useRouter(); - - const onClick = useCallback( - (rid) => (): void => - router.navigate({ - name: 'admin-rooms', - params: { - context: 'edit', - id: rid, - }, - }), - [router], - ); - - const headers = useMemo( - () => - [ - - {t('Name')} - , - - {t('Type')} - , - - {t('Visibility')} - , - - {t('Users')} - , - mediaQuery && ( + const headers = ( + <> + + {t('Name')} + + + {t('Type')} + + + {t('Users')} + + {mediaQuery && ( + <> {t('Msgs')} - ), - mediaQuery && ( void> }): React > {t('Default')} - ), - mediaQuery && ( void> }): React > {t('Featured')} - ), - ].filter(Boolean), - [sortDirection, sortBy, setSort, t, mediaQuery], - ); - - const renderRow = useCallback( - (room: IRoom) => { - const { _id, t: type, usersCount, msgs, default: isDefault, featured, ...args } = room; - const visibility = isPublicRoom(room) ? 'Public' : 'Private'; - const icon = roomCoordinator.getRoomDirectives(room.t).getIcon?.(room); - const roomName = getRoomDisplayName(room); - - return ( - - - - - - - {icon && } - - {roomName} - - - - - - - - {t(getRoomType(room))} - - - - - - {t(visibility)} - - - - {usersCount} - {mediaQuery && {msgs}} - {mediaQuery && {isDefault ? t('True') : t('False')}} - {mediaQuery && {featured ? t('True') : t('False')}} - - ); - }, - [mediaQuery, onClick, t], + + )} + ); - function intersectArraysWithoutDuplicates(array1: IRoom[], array2: IRoom[]) { - const set2 = new Set(array2); - - return [...new Set(array1)].filter((item) => set2.has(item)); - } - - const roomsTypeList = useFilteredTypeRooms(roomFilters.types, isLoading, data?.rooms); - const roomsVisibilityList = useFilteredVisibilityRooms(roomFilters.visibility, isLoading, data?.rooms); - - const roomsList = intersectArraysWithoutDuplicates(roomsTypeList, roomsVisibilityList); - return ( <> - {isLoading && ( {headers} - + )} - {isSuccess && data && data?.rooms.length > 0 && ( + {isSuccess && data.rooms.length === 0 && } + {isSuccess && data.rooms.length > 0 && ( <> {headers} - {isSuccess && roomsList?.map((room) => renderRow(room))} + + {data.rooms?.map((room) => ( + + ))} + void> }): React /> )} - {isSuccess && data && data.rooms.length === 0 && } {isError && ( diff --git a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx index 0d8e5bd0c97e..dede0b34b918 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTableFilters.tsx @@ -12,12 +12,7 @@ const roomTypeFilterStructure = [ isGroupTitle: true, }, { - id: 'channels', - text: 'Channels', - checked: false, - }, - { - id: 'directMessages', + id: 'd', text: 'Direct_Message', checked: false, }, @@ -27,31 +22,23 @@ const roomTypeFilterStructure = [ checked: false, }, { - id: 'omnichannel', + id: 'l', text: 'Omnichannel', checked: false, }, { - id: 'teams', - text: 'Teams', + id: 'p', + text: 'Private_Channels', checked: false, }, -] as OptionProp[]; - -const roomVisibilityFilterStructure = [ { - id: 'filter_by_visibility', - text: 'Filter_by_visibility', - isGroupTitle: true, - }, - { - id: 'private', - text: 'Private', + id: 'c', + text: 'Public_Channels', checked: false, }, { - id: 'public', - text: 'Public', + id: 'teams', + text: 'Teams', checked: false, }, ] as OptionProp[]; @@ -60,13 +47,11 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch(roomTypeFilterStructure); - const [roomVisibilityOptions, setRoomVisibilityOptions] = useState(roomVisibilityFilterStructure); const [roomTypeSelectedOptions, setRoomTypeSelectedOptions] = useState([]); - const [roomVisibilitySelectedOptions, setRoomVisibilitySelectedOptions] = useState([]); useEffect(() => { - return setFilters({ searchText: text, types: roomTypeSelectedOptions, visibility: roomVisibilitySelectedOptions }); - }, [setFilters, roomTypeSelectedOptions, roomVisibilitySelectedOptions, text]); + return setFilters({ searchText: text, types: roomTypeSelectedOptions }); + }, [setFilters, roomTypeSelectedOptions, text]); const handleSearchTextChange = useCallback((event) => setText(event.currentTarget.value), []); @@ -100,17 +85,6 @@ const RoomsTableFilters = ({ setFilters }: { setFilters: Dispatch - - - - ); }; diff --git a/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx b/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx deleted file mode 100644 index 7114aa4f35c9..000000000000 --- a/apps/meteor/client/views/admin/rooms/useFilteredTypeRooms.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { isDiscussion, isTeamRoom, isDirectMessageRoom } from '@rocket.chat/core-typings'; -import type { OptionProp } from '@rocket.chat/ui-client'; - -const filterRoomsByChannels = (room: Partial): boolean => - (room.t === 'c' || room.t === 'p') && !isDiscussion(room) && !isTeamRoom(room); // can be a public channel or a private channel (group) -const filterRoomsByDirectMessages = (room: Partial): boolean => isDirectMessageRoom(room); -const filterRoomsByDiscussions = (room: Partial): boolean => isDiscussion(room); -const filterRoomsByOmnichannel = ({ t }: Partial): boolean => t === 'l'; // LiveChat -const filterRoomsByTeams = (room: Partial): boolean => isTeamRoom(room); - -const filters: Record) => boolean> = { - channels: filterRoomsByChannels, - directMessages: filterRoomsByDirectMessages, - discussions: filterRoomsByDiscussions, - omnichannel: filterRoomsByOmnichannel, - teams: filterRoomsByTeams, -}; - -export const useFilteredTypeRooms = (selectedOptions: OptionProp[], isLoading: boolean, rooms?: IRoom[]) => { - if (isLoading || !rooms) return []; - if (selectedOptions.length === 0) return rooms; - - let filtered: IRoom[] = []; - - selectedOptions.forEach((option) => { - filtered = [...new Set([...filtered, ...rooms.filter(filters[option.id])])]; - }); - - return filtered; -}; diff --git a/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx b/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx deleted file mode 100644 index be4064817001..000000000000 --- a/apps/meteor/client/views/admin/rooms/useFilteredVisibilityRooms.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { isPublicRoom } from '@rocket.chat/core-typings'; -import type { OptionProp } from '@rocket.chat/ui-client'; - -const filterRoomsByPrivate = (room: Partial): boolean => !isPublicRoom(room); -const filterRoomsByPublic = (room: Partial): boolean => isPublicRoom(room); - -const filters: Record) => boolean> = { - private: filterRoomsByPrivate, - public: filterRoomsByPublic, -}; - -export const useFilteredVisibilityRooms = (selectedOptions: OptionProp[], isLoading: boolean, rooms?: IRoom[]) => { - if (isLoading || !rooms) return []; - if (selectedOptions.length === 0) return rooms; - - let filtered: IRoom[] = []; - - selectedOptions.forEach((option) => { - filtered = [...new Set([...filtered, ...rooms.filter(filters[option.id])])]; - }); - - return filtered; -}; diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 34d6a6c4e8ba..f3e5e89c80d6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1622,7 +1622,7 @@ "Direct": "Direct", "Direction": "Direction", "Livechat_Facebook_API_Secret": "OmniChannel API Secret", - "Direct_Message": "Direct Message", + "Direct_Message": "Direct message", "Livechat_Facebook_Enabled": "Facebook integration enabled", "Direct_message_creation_description": "You are about to create a chat with multiple users. Add the ones you would like to talk, everyone in the same place, using direct messages.", "Direct_message_someone": "Direct message someone", @@ -4053,9 +4053,10 @@ "Privacy_summary": "Privacy summary", "Private": "Private", "private": "private", + "Private_channels": "Private channels", "Private_Apps": "Private Apps", "Private_Channel": "Private Channel", - "Private_Channels": "Private Channels", + "Private_Channels": "Private channels", "Private_Chats": "Private Chats", "Private_Group": "Private Group", "Private_Groups": "Private Groups", @@ -4085,7 +4086,7 @@ "Public": "Public", "public": "public", "Public_Channel": "Public Channel", - "Public_Channels": "Public Channels", + "Public_Channels": "Public channels", "Public_Community": "Public Community", "Public_URL": "Public URL", "Purchase_for_free": "Purchase for FREE", From 93d4912e174be7e34f3287e8cb1f176b3514d2bc Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Tue, 12 Sep 2023 01:52:15 -0300 Subject: [PATCH 08/17] fix: missing params on `updateOwnBasicInfo` endpoint (#30156) --- .changeset/wise-walls-tan.md | 6 ++++++ apps/meteor/app/api/server/v1/users.ts | 2 ++ packages/rest-typings/src/v1/users.ts | 16 +++------------- .../users/UsersUpdateOwnBasicInfoParamsPOST.ts | 10 ++++++++++ 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 .changeset/wise-walls-tan.md diff --git a/.changeset/wise-walls-tan.md b/.changeset/wise-walls-tan.md new file mode 100644 index 000000000000..f558de82ec4c --- /dev/null +++ b/.changeset/wise-walls-tan.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +fix: missing params on updateOwnBasicInfo endpoint diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index a4e3f974ac65..b23d41255c3b 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -126,7 +126,9 @@ API.v1.addRoute( realname: this.bodyParams.data.name, username: this.bodyParams.data.username, nickname: this.bodyParams.data.nickname, + bio: this.bodyParams.data.bio, statusText: this.bodyParams.data.statusText, + statusType: this.bodyParams.data.statusType, newPassword: this.bodyParams.data.newPassword, typedPassword: this.bodyParams.data.currentPassword, }; diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 947228476bdd..c47f4be6404d 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -9,7 +9,6 @@ import type { } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; -import type { UsersSendConfirmationEmailParamsPOST } from '..'; import type { PaginatedRequest } from '../helpers/PaginatedRequest'; import type { PaginatedResult } from '../helpers/PaginatedResult'; import type { UserCreateParamsPOST } from './users/UserCreateParamsPOST'; @@ -20,7 +19,9 @@ import type { UserSetActiveStatusParamsPOST } from './users/UserSetActiveStatusP import type { UsersAutocompleteParamsGET } from './users/UsersAutocompleteParamsGET'; import type { UsersInfoParamsGet } from './users/UsersInfoParamsGet'; import type { UsersListTeamsParamsGET } from './users/UsersListTeamsParamsGET'; +import type { UsersSendConfirmationEmailParamsPOST } from './users/UsersSendConfirmationEmailParamsPOST'; import type { UsersSetPreferencesParamsPOST } from './users/UsersSetPreferenceParamsPOST'; +import type { UsersUpdateOwnBasicInfoParamsPOST } from './users/UsersUpdateOwnBasicInfoParamsPOST'; import type { UsersUpdateParamsPOST } from './users/UsersUpdateParamsPOST'; const ajv = new Ajv({ @@ -358,18 +359,7 @@ export type UsersEndpoints = { }; '/v1/users.updateOwnBasicInfo': { - POST: (params: { - data: { - email?: string; - name?: string; - username?: string; - nickname?: string; - statusText?: string; - newPassword?: string; - currentPassword?: string; - }; - customFields?: Record; - }) => { + POST: (params: UsersUpdateOwnBasicInfoParamsPOST) => { user: IUser; }; }; diff --git a/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts index cff6fee56bfa..13c3066e4767 100644 --- a/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersUpdateOwnBasicInfoParamsPOST.ts @@ -10,7 +10,9 @@ export type UsersUpdateOwnBasicInfoParamsPOST = { name?: string; username?: string; nickname?: string; + bio?: string; statusText?: string; + statusType?: string; currentPassword?: string; newPassword?: string; }; @@ -39,6 +41,14 @@ const UsersUpdateOwnBasicInfoParamsPostSchema = { type: 'string', nullable: true, }, + bio: { + type: 'string', + nullable: true, + }, + statusType: { + type: 'string', + nullable: true, + }, statusText: { type: 'string', nullable: true, From b58aee71fcebb41c23a15e1e15594214b35867c3 Mon Sep 17 00:00:00 2001 From: janainaCoelhoRocketchat <105796517+janainaCoelhoRocketchat@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:18:33 -0300 Subject: [PATCH 09/17] test: Changed Home Page spec titles (#30349) --- apps/meteor/tests/e2e/homepage.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/meteor/tests/e2e/homepage.spec.ts b/apps/meteor/tests/e2e/homepage.spec.ts index 380fa54d2af3..4f8f9d09a2f3 100644 --- a/apps/meteor/tests/e2e/homepage.spec.ts +++ b/apps/meteor/tests/e2e/homepage.spec.ts @@ -32,7 +32,7 @@ test.describe.serial('homepage', () => { await adminPage.close(); }); - test('layout', async () => { + test('expect customize button and all cards to be visible', async () => { await test.step('expect show customize button', async () => { await expect(adminPage.locator('role=button[name="Customize"]')).toBeVisible(); }); @@ -47,7 +47,7 @@ test.describe.serial('homepage', () => { await expect((await api.post('/settings/Layout_Home_Body', { value: '' })).status()).toBe(200); }); - test('layout', async () => { + test('visibility and button functionality in custom body with empty custom content', async () => { await test.step('expect default value in custom body', async () => { await expect( adminPage.locator('role=status[name="Admins may insert content html to be rendered in this white space."]'), @@ -70,7 +70,7 @@ test.describe.serial('homepage', () => { await expect((await api.post('/settings/Layout_Home_Body', { value: 'Hello admin' })).status()).toBe(200); }); - test('layout', async () => { + test('visibility and button functionality in custom body with custom content', async () => { await test.step('expect custom body to be visible', async () => { await expect(adminPage.locator('role=status[name="Hello admin"]')).toBeVisible(); }); @@ -122,7 +122,7 @@ test.describe.serial('homepage', () => { await regularUserPage.close(); }); - test('layout', async () => { + test('the option customize is not be active', async () => { await test.step('expect to not show customize button', async () => { await expect(regularUserPage.locator('role=button[name="Customize"]')).not.toBeVisible(); }); @@ -162,7 +162,7 @@ test.describe.serial('homepage', () => { expect((await api.post('/settings/Layout_Home_Title', { value: 'Home' })).status()).toBe(200); }); - test('layout', async () => { + test('expect welcome text and header text to be correct', async () => { await test.step('expect welcome text to be NewSiteName', async () => { await expect(regularUserPage.locator('role=heading[name="Welcome to NewSiteName"]')).toBeVisible(); }); @@ -202,7 +202,7 @@ test.describe.serial('homepage', () => { expect((await api.post('/settings/Layout_Custom_Body_Only', { value: false })).status()).toBe(200); }); - test('layout', async () => { + test('expect default layout not be visible and custom body visible', async () => { await test.step('expect default layout to not be visible', async () => { await expect(regularUserPage.locator('[data-qa-id="homepage-welcome-text"]')).not.toBeVisible(); }); From 1000b9b317f35b0bff4c1981e7a7c52f9e53fe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=AF=E3=83=B3=E3=82=B7=E3=83=A5?= <61188295+Dnouv@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:27:07 +0530 Subject: [PATCH 10/17] fix: apps name misalignment in case of no icons (#30356) --- .changeset/young-trains-glow.md | 5 +++++ .../client/components/message/toolbox/MessageActionMenu.tsx | 1 + .../MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx | 1 + 3 files changed, 7 insertions(+) create mode 100644 .changeset/young-trains-glow.md diff --git a/.changeset/young-trains-glow.md b/.changeset/young-trains-glow.md new file mode 100644 index 000000000000..77f50812143f --- /dev/null +++ b/.changeset/young-trains-glow.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Fixed the issue of apps icon uneven alignment in case of missing icons inside message composer toolbar & message toolbar menu. diff --git a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx index 4c5d442652f2..54a320ebf3d7 100644 --- a/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx +++ b/apps/meteor/client/components/message/toolbox/MessageActionMenu.tsx @@ -101,6 +101,7 @@ const MessageActionMenu = ({ options, onChangeMenuVisibility, ...props }: Messag data-qa-type='message-action' data-qa-id={option.id} role={option.role ? option.role : 'button'} + gap={!option.icon && option.type === 'apps'} /> ))} {index !== arr.length - 1 && } diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx index 021f6518021a..5066ecb192e1 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx @@ -93,6 +93,7 @@ const ActionsToolbarDropdown = ({ isRecording, rid, tmid, actions, ...props }: A chat: chatContext, }) } + gap={!item.icon} > {item.icon && ['name']} />} {item.name} From 091f248a55ac5ca9cea249dbf4196240b6de4993 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 12 Sep 2023 12:19:24 -0300 Subject: [PATCH 11/17] refactor(client): async loading (#30314) Co-authored-by: Guilherme Gazzo --- .../meteor/client/lib/2fa/process2faReturn.ts | 4 +++- apps/meteor/client/main.ts | 24 ++++++++++--------- apps/meteor/client/polyfills/index.ts | 1 - .../client/polyfills/objectFromEntries.ts | 5 ---- .../client/providers/RouterProvider.tsx | 6 ----- 5 files changed, 16 insertions(+), 24 deletions(-) delete mode 100644 apps/meteor/client/polyfills/objectFromEntries.ts diff --git a/apps/meteor/client/lib/2fa/process2faReturn.ts b/apps/meteor/client/lib/2fa/process2faReturn.ts index e5ff73be77b1..95f7f1dcb361 100644 --- a/apps/meteor/client/lib/2fa/process2faReturn.ts +++ b/apps/meteor/client/lib/2fa/process2faReturn.ts @@ -1,10 +1,12 @@ import { SHA256 } from '@rocket.chat/sha256'; import { Meteor } from 'meteor/meteor'; +import { lazy } from 'react'; -import TwoFactorModal from '../../components/TwoFactorModal'; import { imperativeModal } from '../imperativeModal'; import { isTotpInvalidError, isTotpRequiredError } from './utils'; +const TwoFactorModal = lazy(() => import('../../components/TwoFactorModal')); + const twoFactorMethods = ['totp', 'email', 'password'] as const; type TwoFactorMethod = (typeof twoFactorMethods)[number]; diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index bb08f0242d4f..334515980787 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,13 +1,15 @@ -import '../ee/client/ecdh'; -import './polyfills'; +import { FlowRouter } from 'meteor/kadira:flow-router'; -import '../lib/oauthRedirectUriClient'; -import './lib/meteorCallWrapper'; -import './importPackages'; +FlowRouter.wait(); -import '../ee/client'; -import './methods'; -import './startup'; -import './views/admin'; -import './views/marketplace'; -import './views/account'; +FlowRouter.notFound = { + action: () => undefined, +}; + +import('./polyfills') + .then(() => Promise.all([import('./lib/meteorCallWrapper'), import('../lib/oauthRedirectUriClient')])) + .then(() => import('../ee/client/ecdh')) + .then(() => import('./importPackages')) + .then(() => Promise.all([import('./methods'), import('./startup')])) + .then(() => import('../ee/client')) + .then(() => Promise.all([import('./views/admin'), import('./views/marketplace'), import('./views/account')])); diff --git a/apps/meteor/client/polyfills/index.ts b/apps/meteor/client/polyfills/index.ts index 46f5bcb8d68d..f07d828a4602 100644 --- a/apps/meteor/client/polyfills/index.ts +++ b/apps/meteor/client/polyfills/index.ts @@ -4,4 +4,3 @@ import './childNodeRemove'; import './cssVars'; import './customEventPolyfill'; import './hoverTouchClick'; -import './objectFromEntries'; diff --git a/apps/meteor/client/polyfills/objectFromEntries.ts b/apps/meteor/client/polyfills/objectFromEntries.ts deleted file mode 100644 index d59198ebd1d3..000000000000 --- a/apps/meteor/client/polyfills/objectFromEntries.ts +++ /dev/null @@ -1,5 +0,0 @@ -Object.fromEntries = - Object.fromEntries || - function fromEntries(entries: Iterable): { [k: string]: T } { - return [...entries].reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {}); - }; diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index 0dd7ee31deed..0f146ec83128 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -17,12 +17,6 @@ import React from 'react'; import { appLayout } from '../lib/appLayout'; import { queueMicrotask } from '../lib/utils/queueMicrotask'; -FlowRouter.wait(); - -FlowRouter.notFound = { - action: () => undefined, -}; - const subscribers = new Set<() => void>(); const listenToRouteChange = () => { From 8202f2782ce987ed36159bfc1640944c1b7fa41c Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 12 Sep 2023 10:10:21 -0600 Subject: [PATCH 12/17] regression: Reports by-department showing null when department no longer exists (#30350) --- apps/meteor/ee/server/models/raw/LivechatRooms.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/meteor/ee/server/models/raw/LivechatRooms.ts b/apps/meteor/ee/server/models/raw/LivechatRooms.ts index 227da1c98e8c..70824c7d7130 100644 --- a/apps/meteor/ee/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/ee/server/models/raw/LivechatRooms.ts @@ -540,6 +540,13 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo }, }, }, + { + $match: { + _id: { + $ne: null, + }, + }, + }, { $sort: sort || { total: 1 }, }, From 880ab5689cda312826f5bd38b8c98f0d5c2e24ae Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Tue, 12 Sep 2023 16:02:20 -0300 Subject: [PATCH 13/17] fix: selected departments not being displayed due to pagination (#30365) --- .changeset/slimy-cheetahs-heal.md | 5 +++++ .../components/Omnichannel/hooks/useDepartmentsList.ts | 4 ---- .../omnichannel/departments/EditDepartmentWithData.tsx | 2 +- .../omnichannel/additionalForms/DepartmentForwarding.tsx | 7 ++++++- 4 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 .changeset/slimy-cheetahs-heal.md diff --git a/.changeset/slimy-cheetahs-heal.md b/.changeset/slimy-cheetahs-heal.md new file mode 100644 index 000000000000..44233bc87766 --- /dev/null +++ b/.changeset/slimy-cheetahs-heal.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed selected departments not being displayed due to pagination diff --git a/apps/meteor/client/components/Omnichannel/hooks/useDepartmentsList.ts b/apps/meteor/client/components/Omnichannel/hooks/useDepartmentsList.ts index 3b39ea79d42f..fd3c0a29effe 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useDepartmentsList.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useDepartmentsList.ts @@ -20,7 +20,6 @@ type DepartmentListItem = { _id: string; label: string; value: string; - _updatedAt: Date; }; export const useDepartmentsList = ( @@ -66,7 +65,6 @@ export const useDepartmentsList = ( _id, label: department.archived ? `${name} [${t('Archived')}]` : name, value: _id, - _updatedAt: new Date(_updatedAt || ''), }; }); @@ -75,7 +73,6 @@ export const useDepartmentsList = ( _id: '', label: t('All'), value: 'all', - _updatedAt: new Date(), }); options.haveNone && @@ -83,7 +80,6 @@ export const useDepartmentsList = ( _id: '', label: t('None'), value: '', - _updatedAt: new Date(), }); return { diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartmentWithData.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartmentWithData.tsx index 7f5389d0fbe7..64016d23db23 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartmentWithData.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartmentWithData.tsx @@ -22,7 +22,7 @@ const EditDepartmentWithData = ({ id, title }: EditDepartmentWithDataProps) => { }); if (isInitialLoading) { - return ; + return ; } if (isError || (id && !data?.department)) { diff --git a/apps/meteor/ee/client/omnichannel/additionalForms/DepartmentForwarding.tsx b/apps/meteor/ee/client/omnichannel/additionalForms/DepartmentForwarding.tsx index 1d93108debf7..ed9a40e4bd0e 100644 --- a/apps/meteor/ee/client/omnichannel/additionalForms/DepartmentForwarding.tsx +++ b/apps/meteor/ee/client/omnichannel/additionalForms/DepartmentForwarding.tsx @@ -28,6 +28,11 @@ export const DepartmentForwarding = ({ departmentId, value = [], handler, label const { phase: departmentsPhase, items: departmentsItems, itemCount: departmentsTotal } = useRecordList(departmentsList); + const options = useMemo(() => { + const pending = value.filter(({ value }) => !departmentsItems.find((dep) => dep.value === value)); + return [...departmentsItems, ...pending]; + }, [departmentsItems, value]); + return ( {t(label)} @@ -41,7 +46,7 @@ export const DepartmentForwarding = ({ departmentId, value = [], handler, label filter={debouncedDepartmentsFilter} setFilter={setDepartmentsFilter} onChange={handler} - options={departmentsItems} + options={options} value={value} placeholder={t('Select_an_option')} endReached={ From f0c8867bb915edbe5a1cf1d8a8b06d9d2082f6d3 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Tue, 12 Sep 2023 16:48:41 -0300 Subject: [PATCH 14/17] fix: Disabled call to EE tags endpoint when on CE license (#30353) --- .changeset/honest-mirrors-sit.md | 5 +++++ .../Omnichannel/hooks/useLivechatTags.ts | 20 +++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .changeset/honest-mirrors-sit.md diff --git a/.changeset/honest-mirrors-sit.md b/.changeset/honest-mirrors-sit.md new file mode 100644 index 000000000000..4e4298cb8110 --- /dev/null +++ b/.changeset/honest-mirrors-sit.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Disabled call to tags enterprise endpoint when on community license diff --git a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts index 4bd85be40342..ce5704b66482 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts @@ -1,6 +1,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; +import { useOmnichannel } from '../../../hooks/omnichannel/useOmnichannel'; + type Props = { department?: string; text?: string; @@ -9,13 +11,19 @@ type Props = { export const useLivechatTags = (options: Props) => { const getTags = useEndpoint('GET', '/v1/livechat/tags'); + const { isEnterprise } = useOmnichannel(); const { department, text, viewAll } = options; - return useQuery(['/v1/livechat/tags', text, department], () => - getTags({ - text: text || '', - ...(department && { department }), - viewAll: viewAll ? 'true' : 'false', - }), + return useQuery( + ['/v1/livechat/tags', text, department], + () => + getTags({ + text: text || '', + ...(department && { department }), + viewAll: viewAll ? 'true' : 'false', + }), + { + enabled: isEnterprise, + }, ); }; From 3656fd4f26d39142972774cd009345b03419b832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:55:40 -0300 Subject: [PATCH 15/17] chore: a11y color report changes (#30333) --- ee/packages/ui-theming/src/paletteDark.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ee/packages/ui-theming/src/paletteDark.ts b/ee/packages/ui-theming/src/paletteDark.ts index 545fc9f098b8..9898c7fe95f9 100644 --- a/ee/packages/ui-theming/src/paletteDark.ts +++ b/ee/packages/ui-theming/src/paletteDark.ts @@ -120,12 +120,12 @@ export const palette = [ category: 'Button', description: 'Primary Background', list: [ - { name: 'button-background-primary-default', token: '', color: '#3976D1' }, - { name: 'button-background-primary-hover', token: '', color: '#295EAE' }, - { name: 'button-background-primary-press', token: '', color: '#245399' }, - { name: 'button-background-primary-focus', token: '', color: '#3976D1' }, - { name: 'button-background-primary-keyfocus', token: '', color: '#3976D1' }, - { name: 'button-background-primary-disabled', token: '', color: '#1D3963' }, + { name: 'button-background-primary-default', token: '', color: '#095AD2' }, + { name: 'button-background-primary-hover', token: '', color: '#10529E' }, + { name: 'button-background-primary-press', token: '', color: '#01336B' }, + { name: 'button-background-primary-focus', token: '', color: '#095AD2' }, + { name: 'button-background-primary-keyfocus', token: '', color: '#095AD2' }, + { name: 'button-background-primary-disabled', token: '', color: '#012247' }, ], }, { @@ -177,7 +177,7 @@ export const palette = [ list: [ { name: 'button-font-on-primary', token: 'white', color: '#FFFFFF' }, { name: 'button-font-on-secondary', token: 'N400', color: '#E4E7EA' }, - { name: 'button-font-on-secondary-danger', token: '', color: '#C14454' }, + { name: 'button-font-on-secondary-danger', token: '', color: '#FFC1C9' }, { name: 'button-font-on-danger', token: 'white', color: '#FFFFFF' }, { name: 'button-font-on-success', token: 'white', color: '#FFFFFF' }, { name: 'button-font-on-primary-disabled', token: 'N700', color: '#6C727A' }, @@ -185,7 +185,7 @@ export const palette = [ { name: 'button-font-on-secondary-danger-disabled', token: '', - color: '#613339', + color: '#6B0513', }, { name: 'button-font-on-danger-disabled', token: '', color: '#757575' }, { name: 'button-font-on-success-disabled', token: '', color: '#757575' }, From 711bb92ba20af7631128694d99fe038d941cf1b0 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 12 Sep 2023 17:51:14 -0300 Subject: [PATCH 16/17] chore: Revert "fix: update type of message bridge's create method" (#30355) --- .changeset/loud-bees-smoke.md | 5 ----- apps/meteor/app/apps/server/bridges/messages.ts | 4 ++-- apps/meteor/package.json | 2 +- yarn.lock | 14 +++++++------- 4 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 .changeset/loud-bees-smoke.md diff --git a/.changeset/loud-bees-smoke.md b/.changeset/loud-bees-smoke.md deleted file mode 100644 index 7b34a0d58af4..000000000000 --- a/.changeset/loud-bees-smoke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': minor ---- - -New helper for Apps to notify users via a Direct Message diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index 47648e445939..d75a0c244674 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -1,4 +1,4 @@ -import type { IMessage, IDirectMessage } from '@rocket.chat/apps-engine/definition/messages'; +import type { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms'; import type { IUser } from '@rocket.chat/apps-engine/definition/users'; import type { ITypingDescriptor } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; @@ -17,7 +17,7 @@ export class AppMessageBridge extends MessageBridge { super(); } - protected async create(message: IMessage | IDirectMessage, appId: string): Promise { + protected async create(message: IMessage, appId: string): Promise { this.orch.debugLog(`The App ${appId} is creating a new message.`); const convertedMessage = await this.orch.getConverters()?.get('messages').convertAppMessage(message); diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 2e288c8dbd30..4b4d1275656e 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -225,7 +225,7 @@ "@rocket.chat/account-utils": "workspace:^", "@rocket.chat/agenda": "workspace:^", "@rocket.chat/api-client": "workspace:^", - "@rocket.chat/apps-engine": "1.41.0-alpha.325", + "@rocket.chat/apps-engine": "1.41.0-alpha.312", "@rocket.chat/base64": "workspace:^", "@rocket.chat/cas-validate": "workspace:^", "@rocket.chat/core-services": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 100b3ba60fe7..c598977509bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7774,9 +7774,9 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/apps-engine@npm:1.41.0-alpha.325": - version: 1.41.0-alpha.325 - resolution: "@rocket.chat/apps-engine@npm:1.41.0-alpha.325" +"@rocket.chat/apps-engine@npm:1.41.0-alpha.312": + version: 1.41.0-alpha.312 + resolution: "@rocket.chat/apps-engine@npm:1.41.0-alpha.312" dependencies: adm-zip: ^0.5.9 cryptiles: ^4.1.3 @@ -7784,11 +7784,11 @@ __metadata: lodash.clonedeep: ^4.5.0 semver: ^5.7.1 stack-trace: 0.0.10 - uuid: ~8.3.2 + uuid: ^3.4.0 vm2: ^3.9.19 peerDependencies: "@rocket.chat/ui-kit": "*" - checksum: 3159b69d1174166bfe1fea13ac51e81bc39ddcabad3a8dcd20e4614d33592b7f93ae45625578d7545c149f52f90e9c30dee92a1bbd3f5830f7bcdc13d19fcef4 + checksum: 003853d3c4d4374ab984474026e4ae657daf4591fe4c375b914aa57c27f576af0fcba66e70c539e056b5d80a1ef655775f6f3a07bf81a36ab6fd438ce464e70f languageName: node linkType: hard @@ -8556,7 +8556,7 @@ __metadata: "@rocket.chat/account-utils": "workspace:^" "@rocket.chat/agenda": "workspace:^" "@rocket.chat/api-client": "workspace:^" - "@rocket.chat/apps-engine": 1.41.0-alpha.325 + "@rocket.chat/apps-engine": 1.41.0-alpha.312 "@rocket.chat/base64": "workspace:^" "@rocket.chat/cas-validate": "workspace:^" "@rocket.chat/core-services": "workspace:^" @@ -38668,7 +38668,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^8.0.0, uuid@npm:^8.3.1, uuid@npm:^8.3.2, uuid@npm:~8.3.2": +"uuid@npm:^8.0.0, uuid@npm:^8.3.1, uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" bin: From 7137a193a79c544d2d8e26e976e18b545daabcf8 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 12 Sep 2023 17:02:57 -0600 Subject: [PATCH 17/17] feat: Add flag to disable teams mention via troubleshoot (#30372) --- .changeset/nine-bottles-press.md | 5 +++++ .../app/mentions/server/getMentionedTeamMembers.ts | 12 +++++++++++- apps/meteor/app/mentions/server/server.ts | 4 ++++ .../packages/rocketchat-i18n/i18n/en.i18n.json | 2 ++ apps/meteor/server/lib/spotlight.js | 2 +- apps/meteor/server/settings/troubleshoot.ts | 4 ++++ 6 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 .changeset/nine-bottles-press.md diff --git a/.changeset/nine-bottles-press.md b/.changeset/nine-bottles-press.md new file mode 100644 index 000000000000..f9a57fa676ad --- /dev/null +++ b/.changeset/nine-bottles-press.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +feat: Add flag to disable teams mention via troubleshoot page diff --git a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts index 1d36400296b4..b1355c90cb93 100644 --- a/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts +++ b/apps/meteor/app/mentions/server/getMentionedTeamMembers.ts @@ -2,6 +2,7 @@ import { Team } from '@rocket.chat/core-services'; import type { IMessage } from '@rocket.chat/core-typings'; import { callbacks } from '../../../lib/callbacks'; +import { settings } from '../../settings/server'; interface IExtraDataForNotification { userMentions: any[]; @@ -9,7 +10,7 @@ interface IExtraDataForNotification { message: IMessage; } -callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDataForNotification) => { +const beforeGetMentions = async (mentionIds: string[], extra?: IExtraDataForNotification) => { const { otherMentions } = extra ?? {}; const teamIds = otherMentions?.filter(({ type }) => type === 'team').map(({ _id }) => _id); @@ -22,4 +23,13 @@ callbacks.add('beforeGetMentions', async (mentionIds: string[], extra?: IExtraDa mentionIds.push(...new Set(members.map(({ userId }) => userId).filter((userId) => !mentionIds.includes(userId)))); return mentionIds; +}; + +settings.watch('Troubleshoot_Disable_Teams_Mention', (value) => { + if (value) { + callbacks.remove('beforeGetMentions', 'before-get-mentions-get-teams'); + return; + } + + callbacks.add('beforeGetMentions', beforeGetMentions, callbacks.priority.MEDIUM, 'before-get-mentions-get-teams'); }); diff --git a/apps/meteor/app/mentions/server/server.ts b/apps/meteor/app/mentions/server/server.ts index a5b0f89526a2..13765e99d856 100644 --- a/apps/meteor/app/mentions/server/server.ts +++ b/apps/meteor/app/mentions/server/server.ts @@ -25,6 +25,10 @@ export class MentionQueries { type: 'user' as const, })); + if (settings.get('Troubleshoot_Disable_Teams_Mention')) { + return taggedUsers; + } + const taggedTeams = teams.map((team) => ({ ...team, type: 'team' as const, diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index f3e5e89c80d6..a0f6d0e0c6a8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -5178,6 +5178,8 @@ "Troubleshoot_Disable_Statistics_Generator_Alert": "This setting stops the processing all statistics making the info page outdated until someone clicks on the refresh button and may cause other missing information around the system!", "Troubleshoot_Disable_Workspace_Sync": "Disable Workspace Sync", "Troubleshoot_Disable_Workspace_Sync_Alert": "This setting stops the sync of this server with Rocket.Chat's cloud and may cause issues with marketplace and enteprise licenses!", + "Troubleshoot_Disable_Teams_Mention": "Disable Teams mention", + "Troubleshoot_Disable_Teams_Mention_Alert": "This setting disables the teams mention feature. User's won't be able to mention a Team by name in a message and get its members notified.", "True": "True", "Try_now": "Try now", "Try_searching_in_the_marketplace_instead": "Try searching in the Marketplace instead", diff --git a/apps/meteor/server/lib/spotlight.js b/apps/meteor/server/lib/spotlight.js index ae38c903cbe2..62fcdc3a66b4 100644 --- a/apps/meteor/server/lib/spotlight.js +++ b/apps/meteor/server/lib/spotlight.js @@ -144,7 +144,7 @@ export class Spotlight { } async _searchTeams(userId, { text, options, users, mentions }) { - if (!mentions) { + if (!mentions || settings.get('Troubleshoot_Disable_Teams_Mention')) { return users; } diff --git a/apps/meteor/server/settings/troubleshoot.ts b/apps/meteor/server/settings/troubleshoot.ts index ecc0d37dc711..bc1cd1484301 100644 --- a/apps/meteor/server/settings/troubleshoot.ts +++ b/apps/meteor/server/settings/troubleshoot.ts @@ -44,4 +44,8 @@ export const createTroubleshootSettings = () => type: 'boolean', i18nDescription: 'Troubleshoot_Disable_Workspace_Sync_Alert', }); + await this.add('Troubleshoot_Disable_Teams_Mention', false, { + type: 'boolean', + i18nDescription: 'Troubleshoot_Disable_Teams_Mention_Alert', + }); });